Use GetExtremeCaretPosition() for all editable nodes, not just the doc root. b=512295 r=roc

This commit is contained in:
Mats Palmgren 2010-02-09 17:09:59 +01:00
parent 250e32b726
commit 55b5f2a328
13 changed files with 373 additions and 48 deletions

View File

@ -25,7 +25,7 @@
* Håkan Waara <hwaara@chello.se>
* Dan Rosen <dr@netscape.com>
* Daniel Glazman <glazman@netscape.com>
* Mats Palmgren <mats.palmgren@bredband.net>
* Mats Palmgren <matspal@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -3023,52 +3023,19 @@ PresShell::CompleteMove(PRBool aForward, PRBool aExtend)
{
// Beware! This may flush notifications via synchronous
// ScrollSelectionIntoView.
nsIContent* root = mSelection->GetAncestorLimiter();
nsIDocument* doc;
if (root && (doc = root->GetOwnerDoc()) && doc->GetRootContent() != root) {
// Make the caret be either at the very beginning (0) or the very end of
// root. Only do this when not moving to the beginning or end of the
// document (root is null or root is the documentElement), that's handled
// below by moving to beginning or end of the scrollable view.
nsIContent* node = root;
PRInt32 offset = 0;
nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
if (aForward) {
nsIContent* next = node;
PRUint32 count;
while ((count = next->GetChildCount()) > 0) {
node = next;
offset = count;
next = next->GetChildAt(count - 1);
}
if (offset > 0 && node->GetChildAt(offset - 1)->Tag() == nsGkAtoms::br) {
--offset;
hint = nsFrameSelection::HINTRIGHT; // for bug 106855
}
}
mSelection->HandleClick(node, offset, offset, aExtend, PR_FALSE, hint);
// HandleClick resets ancestorLimiter, so set it again.
mSelection->SetAncestorLimiter(root);
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return
ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION,
PR_TRUE);
}
nsIFrame *frame = FrameConstructor()->GetRootElementFrame();
nsIContent* limiter = mSelection->GetAncestorLimiter();
nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
: FrameConstructor()->GetRootElementFrame();
if (!frame)
return NS_ERROR_FAILURE;
nsPeekOffsetStruct pos = frame->GetExtremeCaretPosition(!aForward);
mSelection->HandleClick(pos.mResultContent ,pos.mContentOffset ,pos.mContentOffset/*End*/ ,aExtend, PR_FALSE, aForward);
mSelection->HandleClick(pos.mResultContent, pos.mContentOffset,
pos.mContentOffset, aExtend, PR_FALSE, aForward);
if (limiter) {
// HandleClick resets ancestorLimiter, so set it again.
mSelection->SetAncestorLimiter(limiter);
}
// After ScrollSelectionIntoView(), the pending notifications might be
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,

View File

@ -91,6 +91,16 @@ _TEST_FILES = \
test_bug435293-interaction.html \
test_bug435293-skew.html \
test_bug495648.xul \
test_reftests_with_caret.html \
bug106855-1.html \
bug106855-2.html \
bug106855-1-ref.html \
bug482484.html \
bug482484-ref.html \
bug512295-1.html \
bug512295-1-ref.html \
bug512295-2.html \
bug512295-2-ref.html \
test_bug514127.html \
test_bug518777.html \
test_flush_on_paint.html \

View File

@ -0,0 +1,28 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
x<br>
<textarea id="t" rows="4">
A
</textarea><br>
y
<script>
// Position the caret at the last line
var sel = window.getSelection();
sel.removeAllRanges();
var area = document.getElementById('t');
area.focus();
sendKey('RIGHT', window); // now after "A"
sendKey('RIGHT', window); //
sendKey('RIGHT', window); //
sendKey('RIGHT', window); // now at the last line
</script>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
x<br>
<textarea id="t" rows="4">
A
</textarea><br>
y
<script>
// Position the caret at the last line
var sel = window.getSelection();
sel.removeAllRanges();
var area = document.getElementById('t');
area.focus();
sendKey('DOWN', window); // now after "A"
sendKey('DOWN', window); //
sendKey('DOWN', window); // now at the last line
</script>
</body>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
x<br>
<textarea id="t" rows="4">
A
</textarea><br>
y
<script>
// Position the caret at the last line
var sel = window.getSelection();
sel.removeAllRanges();
var area = document.getElementById('t');
area.focus();
sendKey('DOWN', window); // now after "A"
sendKey('DOWN', window); //
sendKey('DOWN', window); //
sendKey('DOWN', window); // now at the last line
</script>
</body>
</html>

View File

@ -0,0 +1,18 @@
<!DOCTYPE HTML><html><head></head>
<body>
<div contentEditable="true" id="div"><p id="p">ABC</p></div>
<script>
// Position the caret after the "A"
var div = document.getElementById('div');
var p = document.getElementById('p');
div.focus();
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(p.firstChild, 1)
range.setEnd(p.firstChild, 1);
sel.addRange(range);
</script>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div contentEditable="true" id="div"><p id="p">BC</p></div>
<script>
// Position the caret before the "B"
var div = document.getElementById('div');
div.focus();
var p = document.getElementById('p');
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(p.firstChild, 0)
range.setEnd(p.firstChild, 0);
sel.addRange(range);
sendKey('UP', div); // move UP
sendChar('A', div); // insert "A"
</script>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div contenteditable="true">
<p id="p">A B CD EFG<br>
1234567890</p>
</div>
x
<script>
// Position the caret at the end of the P element
var p = document.getElementById('p');
var div = p.parentNode;
div.focus();
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(p, p.childNodes.length);
range.setEnd(p, p.childNodes.length);
sel.addRange(range);
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
<div contenteditable="true">
<p id="p">A B CD EFG<br>
1234567890</p>
</div>
x
<script>
// Position the caret after "A"
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
var p = document.getElementById('p');
var t = p.firstChild;
range.setStart(t, 1);
range.setEnd(t, 1);
sel.addRange(range);
p.parentNode.focus();
sendKey('DOWN', window); // now after "1"
sendKey('DOWN', window); // now make sure we get to the end
sendKey('DOWN', window); // now make sure we get to the end
sendKey('DOWN', window); // now make sure we get to the end
sendKey('DOWN', window); // now make sure we get to the end
sendKey('DOWN', window); // now make sure we get to the end
</script>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
x
<div contenteditable="true">
<p id="p">A B CD EFG<br>
1234567890</p>
</div>
<script>
// Position the caret before the "A"
var p = document.getElementById('p');
var div = p.parentNode;
div.focus();
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
range.setStart(p.firstChild, 0);
range.setEnd(p.firstChild, 0);
sel.addRange(range);
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML><html><head>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
</head>
<body>
x
<div contenteditable="true">
<p id="p">A B CD EFG<br>
1234567890</p>
</div>
<script>
// Position the caret after "A"
var sel = window.getSelection();
sel.removeAllRanges();
var range = document.createRange();
var p = document.getElementById('p');
var t = p.firstChild;
range.setStart(t, 1);
range.setEnd(t, 1);
sel.addRange(range);
p.parentNode.focus();
sendKey('DOWN', window); // now after "1"
sendKey('DOWN', window); // now below the P element
sendKey('UP', window); // now before the "1"
sendKey('UP', window); // now before the "A"
sendKey('UP', window); // now before the "A"
sendKey('UP', window); // now before the "A"
</script>
</body>
</html>

View File

@ -0,0 +1,112 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Reftests with caret drawing</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
var canvases = [];
function callbackTestCanvas(canvas)
{
canvases.push(canvas);
if (canvases.length != 2)
return;
var result = canvases[0];
var reference = canvases[1];
var ret = compareSnapshots(result.snapshot, reference.snapshot, true);
ok(ret[0], "Reftest " + result.src +
(ret[0] ? "" : (" FAILED\n" +
"RESULT=" + ret[1] + "\n" +
"REFERENCE=" + ret[2] + "\n")));
// Remove the iframes if the test was successful
if (ret[0]) {
result.parentNode.removeChild(result);
reference.parentNode.removeChild(reference);
}
canvases = [];
nextTest();
}
function doSnapShot(iframe) {
iframe.snapshot = snapshotWindow(iframe.contentWindow, true);
callbackTestCanvas(iframe);
};
function remotePageLoaded() {
var iframe = this;
setTimeout(function(){doSnapShot(iframe);}, 0)
};
function createIframe(url,next) {
var iframe = document.createElement("iframe");
iframe.src = url;
iframe.remotePageLoaded = remotePageLoaded;
var me = this;
iframe.addEventListener("load", function() {
iframe.remotePageLoaded();
if (next) setTimeout(function(){createIframe(next,null);}, 0)
}, false);
window.document.body.appendChild(iframe);
};
function refTest(test,ref) {
createIframe(test,ref);
};
var caretBlinkTime;
function endTest() {
SimpleTest.finish();
netscape.security.PrivilegeManager.enablePrivilege(
'UniversalPreferencesRead UniversalPreferencesWrite UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.setIntPref("ui.caretBlinkTime", caretBlinkTime);
}
var tests = [
[ 'bug106855-1.html' , 'bug106855-1-ref.html' ] ,
[ 'bug106855-2.html' , 'bug106855-1-ref.html' ] ,
[ 'bug482484.html' , 'bug482484-ref.html' ] ,
[ 'bug512295-1.html' , 'bug512295-1-ref.html' ] ,
[ 'bug512295-2.html' , 'bug512295-2-ref.html' ]
];
var testIndex = 0;
function nextTest() {
if (testIndex < tests.length) {
refTest(tests[testIndex][0],tests[testIndex][1]);
++testIndex;
} else {
endTest();
}
}
function runTests() {
try {
SimpleTest.waitForExplicitFinish();
netscape.security.PrivilegeManager.enablePrivilege(
'UniversalPreferencesRead UniversalPreferencesWrite UniversalXPConnect');
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
caretBlinkTime = prefs.getIntPref("ui.caretBlinkTime");
prefs.setIntPref("ui.caretBlinkTime", -1);
nextTest();
} catch(e) {
endTest();
}
}
</script>
</head>
<body onload="runTests()">
</body>
</html>

View File

@ -10,7 +10,7 @@ try {
gWindowUtils = null;
}
function snapshotWindow(win) {
function snapshotWindow(win, withCaret) {
var el = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
el.width = win.innerWidth;
el.height = win.innerHeight;
@ -18,9 +18,11 @@ function snapshotWindow(win) {
// drawWindow requires privileges
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
el.getContext("2d").drawWindow(win, win.scrollX, win.scrollY,
win.innerWidth, win.innerHeight,
"rgb(255,255,255)");
var ctx = el.getContext("2d");
ctx.drawWindow(win, win.scrollX, win.scrollY,
win.innerWidth, win.innerHeight,
"rgb(255,255,255)",
withCaret ? ctx.DRAWWINDOW_DRAW_CARET : 0);
return el;
}