Bug 824965: Implement a method of getting correct CaretPosition from within anonymous content nodes. [r=ehsan]

This commit is contained in:
Scott Johnson 2013-04-11 10:12:49 -05:00
Родитель 0fa8775c2d
Коммит db66a98bce
4 изменённых файлов: 84 добавлений и 12 удалений

Просмотреть файл

@ -97,6 +97,7 @@ class nsScriptObjectTracer;
class nsStringHashKey;
class nsTextFragment;
class nsViewportInfo;
class nsIFrame;
struct JSContext;
struct JSPropertyDescriptor;
@ -2115,6 +2116,21 @@ public:
int32_t& aOutStartOffset,
int32_t& aOutEndOffset);
/**
* Takes a frame for anonymous content within a text control (<input> or
* <textarea>), and returns an offset in the text content, adjusted for a
* trailing <br> frame.
*
* @param aOffsetFrame Frame for the text content in which the offset
* lies
* @param aOffset Offset as calculated by GetContentOffsetsFromPoint
* @param aOutOffset Output adjusted offset
*
* @see GetSelectionInTextControl for the original basis of this function.
*/
static int32_t GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
int32_t aOffset);
static nsIEditor* GetHTMLEditor(nsPresContext* aPresContext);
/**

Просмотреть файл

@ -101,6 +101,7 @@
#include "nsIFormControl.h"
#include "nsIForm.h"
#include "nsIFragmentContentSink.h"
#include "nsIFrame.h"
#include "nsIHTMLDocument.h"
#include "nsIIdleService.h"
#include "nsIImageLoadingContent.h"
@ -6750,6 +6751,40 @@ nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
}
}
// static
int32_t
nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
int32_t aOffset)
{
// The structure of the anonymous frames within a text control frame is
// an optional block frame, followed by an optional br frame.
// If the offset frame has a child, then this frame is the block which
// has the text frames (containing the content) as its children. This will
// be the case if we click to the right of any of the text frames, or at the
// bottom of the text area.
nsIFrame* firstChild = aOffsetFrame->GetFirstPrincipalChild();
if (firstChild) {
// In this case, the passed-in offset is incorrect, and we want the length
// of the entire content in the text control frame.
return firstChild->GetContent()->Length();
}
if (aOffsetFrame->GetPrevSibling() &&
!aOffsetFrame->GetNextSibling()) {
// In this case, we're actually within the last frame, which is a br
// frame. Our offset should therefore be the length of the first child of
// our parent.
int32_t aOutOffset =
aOffsetFrame->GetParent()->GetFirstPrincipalChild()->GetContent()->Length();
return aOutOffset;
}
// Otherwise, we're within one of the text frames, in which case our offset
// has already been correctly calculated.
return aOffset;
}
// static
void
nsContentUtils::GetSelectionInTextControl(Selection* aSelection,

Просмотреть файл

@ -202,6 +202,9 @@
#include "nsDOMEvent.h"
#include "nsIContentPermissionPrompt.h"
#include "mozilla/StaticPtr.h"
#include "nsITextControlElement.h"
#include "nsIDOMNSEditableElement.h"
#include "nsIEditor.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -9318,7 +9321,10 @@ nsIDocument::CaretPositionFromPoint(float aX, float aY)
nsCOMPtr<nsIContent> node = offsets.content;
uint32_t offset = offsets.offset;
if (node && node->IsInNativeAnonymousSubtree()) {
nsCOMPtr<nsIContent> anonNode = node;
bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
if (nodeIsAnonymous) {
node = ptFrame->GetContent();
nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
@ -9326,6 +9332,7 @@ nsIDocument::CaretPositionFromPoint(float aX, float aY)
if (textArea || (input &&
NS_SUCCEEDED(input->MozIsTextField(false, &isText)) &&
isText)) {
offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
node = nonanon;
} else {
node = nullptr;

Просмотреть файл

@ -21,17 +21,31 @@
border: 8px solid black;
width: 600px;
}
#dp {
position: absolute;
width: 2px;
height: 2px;
left: 0px;
top: 0px;
background-color: orange;
}
</style>
<script>
function displayPoint(aX, aY) {
document.getElementById('dp').style['left'] = aX + "px";
document.getElementById('dp').style['top'] = aY + "px";
}
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) {
function checkOffsetsFromPoint(aX, aY, aExpected, aElementName='no-name') {
var cp = document.caretPositionFromPoint(aX, aY);
ok(aExpected == cp.offset, 'expected offset at (' + aX + ', ' + aY + '): ' + aExpected + ', got: ' + cp.offset);
ok(aExpected == cp.offset, 'expected offset at (' + aX + ', ' + aY + ') [' + aElementName + ']: ' + aExpected + ', got: ' + cp.offset);
}
function doTesting() {
@ -39,8 +53,8 @@
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);
checkOffsetsFromPoint(Math.round(test1Rect.left + 1), Math.round(test1Rect.top + 1), 0, 'test1');
checkOffsetsFromPoint(Math.round(test1Rect.left + test1Rect.width - 1), Math.round(test1Rect.top + 1), 13, 'test1');
// Check a middle character in the second line of the div.
// To do this, we calculate 7em in from the left of the bounding
@ -49,20 +63,19 @@
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);
checkOffsetsFromPoint(Math.round(test2Rect.left + pixelsLeft + 1), Math.round(test2Rect.top + 1), 7, 'test2');
// 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);
checkOffsetsFromPoint(test3Rect.left + 5, test3Rect.top + 5, 0, 'test3');
checkOffsetsFromPoint(Math.round(test3Rect.left + test3Rect.width - 15), Math.round(test3Rect.top + 5), 3, 'test3');
// 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);
checkOffsetsFromPoint(test4Rect.left + 5, test4Rect.top + 5, 0, 'test4');
checkOffsetsFromPoint(Math.round(test4Rect.left + test4Rect.width - 10), Math.round(test4Rect.top + 10), 6, 'test4');
// Check to make sure that x or y outside the viewport returns null.
var nullCp1 = document.caretPositionFromPoint(-10, 0);
@ -82,9 +95,10 @@
</script>
</head>
<body onload="doTesting();">
<div id="dp"></div>
<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>
<textarea id="test3">abc</textarea><input id="test4" value="abcdef"><br><br>
<marquee>marquee</marquee>
</div>
</body>