Bug 654352: Implement (blassey) and fix (jwir3) document.caretPositionFromPoint so that it utilizes frame-relative coordinates to get caret position. (original: [r=smaug,roc]) (fixes: [r=blassey,Ms2ger])
authorScott Johnson <sjohnson@mozilla.com>
Fri, 28 Dec 2012 11:11:06 -0600
changeset 126268 c3cc198a6b83735e111bdcd2ba5657512dc10b40
parent 126267 d924c6aa0a8fa538a5cc5803b3e1060912aae4b4
child 126269 b3e769659c3f7e858a95dcd1f1a90eef3b8ec5d6
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, roc, blassey, Ms2ger
bugs654352
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 654352: Implement (blassey) and fix (jwir3) document.caretPositionFromPoint so that it utilizes frame-relative coordinates to get caret position. (original: [r=smaug,roc]) (fixes: [r=blassey,Ms2ger])
content/base/src/Makefile.in
content/base/src/nsDOMCaretPosition.cpp
content/base/src/nsDOMCaretPosition.h
content/base/src/nsDocument.cpp
content/base/test/Ahem.ttf
content/base/test/Makefile.in
content/base/test/test_bug654352.html
dom/bindings/Bindings.conf
dom/interfaces/core/nsIDOMDocument.idl
dom/webidl/CaretPosition.webidl
dom/webidl/WebIDL.mk
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -72,16 +72,17 @@ CPPSRCS		= \
 		nsContentUtils.cpp \
 		nsCopySupport.cpp \
 		nsCrossSiteListenerProxy.cpp \
 		nsCSPService.cpp \
 		nsDataDocumentContentPolicy.cpp \
 		nsDOMAttribute.cpp \
 		nsDOMAttributeMap.cpp \
 		nsDOMBlobBuilder.cpp \
+		nsDOMCaretPosition.cpp \
 		nsDOMDocumentType.cpp \
 		nsDOMFile.cpp \
 		nsDOMFileReader.cpp \
 		nsDOMLists.cpp \
 		nsDOMParser.cpp \
 		nsDOMSerializer.cpp \
 		nsDOMTokenList.cpp \
 		nsDOMSettableTokenList.cpp \
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsDOMCaretPosition.cpp
@@ -0,0 +1,40 @@
+/* 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 "nsDOMCaretPosition.h"
+#include "mozilla/dom/CaretPositionBinding.h"
+#include "nsContentUtils.h"
+
+nsDOMCaretPosition::nsDOMCaretPosition(nsINode* aNode, uint32_t aOffset)
+  : mOffset(aOffset), mOffsetNode(aNode)
+{
+  SetIsDOMBinding();
+}
+
+nsDOMCaretPosition::~nsDOMCaretPosition()
+{
+}
+
+nsINode* nsDOMCaretPosition::GetOffsetNode() const
+{
+  return mOffsetNode;
+}
+
+JSObject*
+nsDOMCaretPosition::WrapObject(JSContext *aCx, JSObject *aScope,
+                              bool *aTried)
+{
+  return mozilla::dom::CaretPositionBinding::Wrap(aCx, aScope, this, aTried);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCaretPosition, mOffsetNode)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCaretPosition)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCaretPosition)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCaretPosition)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
new file mode 100644
--- /dev/null
+++ b/content/base/src/nsDOMCaretPosition.h
@@ -0,0 +1,63 @@
+/* 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/. */
+
+#ifndef nsDOMCaretPosition_h
+#define nsDOMCaretPosition_h
+
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+#include "nsINode.h"
+#include "nsWrapperCache.h"
+
+/**
+ * Implementation of a DOM Caret Position, which is a node and offset within
+ * that node, in the DOM tree.
+ *
+ * http://www.w3.org/TR/cssom-view/#dom-documentview-caretrangefrompoint
+ *
+ * @see Document::caretPositionFromPoint(float x, float y)
+ */
+class nsDOMCaretPosition : public nsISupports,
+                           public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCaretPosition)
+
+  nsDOMCaretPosition(nsINode* aNode, uint32_t aOffset);
+
+  /**
+   * Retrieve the offset (character position within the DOM node) of the
+   * CaretPosition.
+   *
+   * @returns The offset within the DOM node.
+   */
+  uint32_t Offset() const { return mOffset; }
+
+  /**
+   * Retrieve the DOM node with which this CaretPosition was established.
+   * Normally, this will be created from a point, so it will be the DOM
+   * node that lies at the point specified.
+   *
+   * @returns The DOM node of the CaretPosition.
+   *
+   * @see Document::caretPositionFromPoint(float x, float y)
+   */
+  nsINode* GetOffsetNode() const;
+
+  nsISupports* GetParentObject() const
+  {
+    return GetOffsetNode();
+  }
+
+  virtual JSObject* WrapObject(JSContext *aCx, JSObject *aScope, bool *aTried)
+    MOZ_OVERRIDE MOZ_FINAL;
+
+protected:
+  virtual ~nsDOMCaretPosition();
+  uint32_t mOffset;
+  nsCOMPtr<nsINode> mOffsetNode;
+};
+#endif
+
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -179,16 +179,19 @@
 
 #include "imgILoader.h"
 #include "imgRequestProxy.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsSandboxFlags.h"
 #include "nsIAppsService.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DocumentFragment.h"
+#include "nsFrame.h" 
+#include "nsDOMCaretPosition.h"
+#include "nsIDOMHTMLTextAreaElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 // Reference to the document which requested DOM full-screen mode.
 nsWeakPtr nsDocument::sFullScreenDoc = nullptr;
@@ -8815,16 +8818,74 @@ ResetFullScreen(nsIDocument* aDocument, 
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
 
     aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
   }
   return true;
 }
 
+NS_IMETHODIMP
+nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
+{
+  NS_ENSURE_ARG_POINTER(aCaretPos);
+  *aCaretPos = nullptr;
+
+  nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
+  nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
+  nsPoint pt(x, y);
+
+  nsIPresShell *ps = GetShell();
+  if (!ps) {
+    return NS_OK;
+  }
+
+  nsIFrame *rootFrame = ps->GetRootFrame();
+
+  // XUL docs, unlike HTML, have no frame tree until everything's done loading
+  if (!rootFrame) {
+    return NS_OK; // return null to premature XUL callers as a reminder to wait
+  }
+
+  nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, true,
+                                                      false);
+  if (!ptFrame) {
+    return NS_OK;
+  }
+
+  // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
+  // to adjust to frame-relative coordinates before we can perform this call.
+  // It should also not take into account the padding of the frame.
+  nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
+
+  nsFrame::ContentOffsets offsets =
+    ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
+
+  nsCOMPtr<nsIContent> node = offsets.content;
+  uint32_t offset = offsets.offset;
+  if (node && node->IsInNativeAnonymousSubtree()) {
+    nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
+    nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
+    nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
+    bool isText;
+    if (textArea || (input &&
+                     NS_SUCCEEDED(input->MozIsTextField(false, &isText)) &&
+                     isText)) {
+      node = nonanon;
+    } else {
+      node = nullptr;
+      offset = 0;
+    }
+  }
+
+  *aCaretPos = new nsDOMCaretPosition(node, offset);
+  NS_ADDREF(*aCaretPos);
+  return NS_OK;
+}
+
 /* static */
 void
 nsDocument::ExitFullScreen()
 {
   // Clear full-screen stacks in all descendant documents.
   nsCOMPtr<nsIDocument> root(do_QueryReferent(sFullScreenRootDoc));
   if (!root) {
     // Not in full-screen mode.
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ac81cb03165ab831a36abb59145ff7a2f5675fb9
GIT binary patch
literal 12480
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
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -501,16 +501,18 @@ MOCHITEST_FILES_B = \
 		somedatas.resource^headers^ \
 		delayedServerEvents.sjs \
 		test_html_in_xhr.html \
 		file_html_in_xhr.html \
 		file_html_in_xhr2.html \
 		file_html_in_xhr3.html \
 		file_html_in_xhr.sjs \
 		test_bug647518.html \
+		test_bug654352.html \
+                Ahem.ttf \
 		test_bug664916.html \
 		test_bug666604.html \
 		test_bug675121.html \
 		file_bug675121.sjs \
 		test_bug675166.html \
 		test_bug682554.html \
 		test_bug682592.html \
 		bug682592-subframe.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug654352.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<html>
+<!--
+  https://bugzilla.mozilla.org/show_bug.cgi?id=654352
+-->
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <title>Test for Bug 654352</title>
+  <style>
+    @font-face {
+      font-family: Ahem;
+      src: url("Ahem.ttf");
+    }
+
+    #a {
+      font-family: Ahem;
+      padding: 10px;
+      border: 8px solid black;
+      width: 600px;
+    }
+  </style>
+<script>
+  function convertEmToPx(aEm) {
+    // Assumes our base font size is 16px = 12pt = 1.0em.
+    var pxPerEm = 16.0 / 1.0;
+    return pxPerEm * aEm;
+  }
+
+  function checkOffsetsFromPoint(aX, aY, aExpected) {
+    var cp = document.caretPositionFromPoint(aX, aY);
+    ok(aExpected == cp.offset, 'expected offset at (' + aX + ', ' + aY + '): ' + aExpected + ', got: ' + cp.offset);
+  }
+
+  function doTesting() {
+    var test1Element = document.getElementById("test1");
+    var test1Rect = test1Element.getBoundingClientRect();
+
+    // Check the first and last characters of the basic div.
+    checkOffsetsFromPoint(Math.round(test1Rect.left + 1), Math.round(test1Rect.top + 1), 0);
+    checkOffsetsFromPoint(Math.round(test1Rect.left + test1Rect.width - 1), Math.round(test1Rect.top + 1), 13);
+
+    // Check a middle character in the second line of the div.
+    // To do this, we calculate 7em in from the left of the bounding
+    // box, and convert this to PX. (Hence the reason we need the AHEM
+    // font).
+    var pixelsLeft = convertEmToPx(7);
+    var test2Element = document.getElementById("test2");
+    var test2Rect = test2Element.getBoundingClientRect();
+    checkOffsetsFromPoint(Math.round(test2Rect.left + pixelsLeft + 1), Math.round(test2Rect.top + 1), 7);
+
+    // Check the first and last characters of the textarea.
+    var test3Element = document.getElementById('test3');
+    var test3Rect = test3Element.getBoundingClientRect();
+    checkOffsetsFromPoint(test3Rect.left + 1, test3Rect.top + 1, 0);
+    // xxxTODO: This test is disabled due to bug 824965.
+    //checkOffsetsFromPoint(Math.round(test3Rect.left + test3Rect.width - 1), Math.round(test3Rect.top + 1), 0);
+
+    // Check the first and last characters of the input.
+    var test4Element = document.getElementById('test4');
+    var test4Rect = test4Element.getBoundingClientRect();
+    checkOffsetsFromPoint(test4Rect.left + 1, test4Rect.top + 1, 0);
+    checkOffsetsFromPoint(Math.round(test4Rect.left + convertEmToPx(3)), Math.round(test4Rect.top + 10), 3);
+
+    // Check the first and last characters of the marquee.
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+</script>
+</head>
+<body onload="doTesting();">
+<div id="a" contenteditable><span id="test1">abc, abc, abc</span><br>
+<span id="test2" style="color: blue;">abc, abc, abc</span><br>
+<textarea id="test3">abc</textarea><input id="test4" value="abc"><br><br>
+<marquee>marquee</marquee>
+</div>
+</body>
+</html>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -133,16 +133,20 @@ DOMInterfaces = {
     ],
     'resultNotAddRefed': [ 'canvas' ],
     'binaryNames': {
         'mozImageSmoothingEnabled': 'imageSmoothingEnabled',
         'mozFillRule': 'fillRule'
     }
 },
 
+'CaretPosition' : {                                                            
+    'nativeType': 'nsDOMCaretPosition',                                        
+},                                                                             
+
 'ClientRectList': {
     'nativeType': 'nsClientRectList',
     'headerFile': 'nsClientRect.h',
     'resultNotAddRefed': [ 'item' ]
 },
 
 'CSS': {
     'concrete': False,
--- a/dom/interfaces/core/nsIDOMDocument.idl
+++ b/dom/interfaces/core/nsIDOMDocument.idl
@@ -22,17 +22,17 @@ interface nsIDOMLocation;
  * cannot exist outside the context of a Document, the nsIDOMDocument 
  * interface also contains the factory methods needed to create these 
  * objects.
  *
  * For more information on this interface please see 
  * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html
  */
 
-[scriptable, uuid(31ce7ae7-15d5-4fc8-912b-ae0e23e93146)]
+[scriptable, uuid(9b93f82b-9691-4021-8f45-1bf505db77ba)]
 interface nsIDOMDocument : nsIDOMNode
 {
   readonly attribute nsIDOMDocumentType         doctype;
   readonly attribute nsIDOMDOMImplementation    implementation;
   readonly attribute nsIDOMElement              documentElement;
   nsIDOMElement                 createElement([Null(Stringify)] in DOMString tagName)
                                   raises(DOMException);
   nsIDOMDocumentFragment        createDocumentFragment();
@@ -322,17 +322,17 @@ interface nsIDOMDocument : nsIDOMNode
    * @param aImageElement a DOM element to be used as the source image of
    * |-moz-element(#aImageElementId)|. If this is null, the function will
    * unregister the image element ID |aImageElementId|.
    *
    * @see <https://developer.mozilla.org/en/DOM/document.mozSetImageElement>
    */
   void mozSetImageElement(in DOMString aImageElementId,
                           in nsIDOMElement aImageElement);
-  
+
   /**
    * Element which is currently the full-screen element as per the DOM
    * full-screen api.
    *
    * @see <https://wiki.mozilla.org/index.php?title=Gecko:FullScreenAPI>
    */
   readonly attribute nsIDOMElement mozFullScreenElement;
 
@@ -365,16 +365,27 @@ interface nsIDOMDocument : nsIDOMNode
    * The element to which the mouse pointer is locked, if any, as per the
    * DOM pointer lock api.
    *
    * @see <http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html>
    */
   readonly attribute nsIDOMElement mozPointerLockElement;
 
   /**
+   * Retrieve the location of the caret position (DOM node and character
+   * offset within that node), given a point.
+   *
+   * @param x Horizontal point at which to determine the caret position, in
+   *          page coordinates.
+   * @param y Vertical point at which to determine the caret position, in
+   *          page coordinates.
+   */
+  nsISupports /* CaretPosition */ caretPositionFromPoint(in float x, in float y);
+
+  /**
    * Exit pointer is lock if locked, as per the DOM pointer lock api.
    *
    * @see <http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html>
    */
   void mozExitPointerLock();
 
   /**
    * Inline event handler for readystatechange events.
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CaretPosition.webidl
@@ -0,0 +1,12 @@
+/* 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/. */
+
+interface CaretPosition {
+
+  /**
+   * The offsetNode could potentially be null due to anonymous content.
+   */
+  readonly attribute Node? offsetNode;
+  readonly attribute unsigned long offset;
+};
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -15,16 +15,17 @@ webidl_files = \
   AudioDestinationNode.webidl \
   AudioListener.webidl \
   AudioNode.webidl \
   AudioParam.webidl \
   AudioSourceNode.webidl \
   BiquadFilterNode.webidl \
   Blob.webidl \
   CanvasRenderingContext2D.webidl \
+  CaretPosition.webidl \
   ClientRectList.webidl \
   CSS.webidl \
   CSSPrimitiveValue.webidl \
   CSSStyleDeclaration.webidl \
   CSSValue.webidl \
   CSSValueList.webidl \
   DelayNode.webidl \
   Document.webidl \