Bug 1287655 - place textarea/input cursor at end of text when initialized; r=smaug

MozReview-Commit-ID: 2srGXFmla07

--HG--
extra : transplant_source : %3Cn%D30%86%24%82%90%29%191%9C%8A%EB%0D%5D%E2%20%22%E5
This commit is contained in:
Decky Coss 2016-07-21 14:52:49 -04:00
Родитель 2869846cdb
Коммит b69450d2ea
21 изменённых файлов: 146 добавлений и 92 удалений

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

@ -68,16 +68,16 @@
new ExpectedEditState({
editing: true,
multiline: true,
atStart: true,
atEnd: false
atStart: false,
atEnd: true
}),
new ExpectedCursorChange(
['Please refrain from Mayoneggs during this salmonella scare.',
{string: 'textarea'}]),
new ExpectedTextSelectionChanged(0, 0)
new ExpectedTextSelectionChanged(59, 59)
],
[ContentMessages.activateCurrent(10),
new ExpectedTextCaretChanged(0, 10),
new ExpectedTextCaretChanged(10, 59),
new ExpectedEditState({ editing: true,
multiline: true,
atStart: false,
@ -136,6 +136,7 @@
atStart: true,
atEnd: true
}, { focused: 'input[type=text]' }),
new ExpectedTextSelectionChanged(0, 0),
new ExpectedTextSelectionChanged(0, 0)
],
[ContentMessages.simpleMovePrevious,
@ -147,7 +148,8 @@
multiline: false,
atStart: true,
atEnd: false
}, { focused: 'html' })],
},{ focused: 'html' })
],
[ContentMessages.simpleMoveNext,
new ExpectedCursorChange(
[{ string : 'entry' }],

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

@ -119,7 +119,7 @@ function setPermissions() {
input.onblur = function() {
// "Expected" lost of focus since the test is finished.
if (input.value === '#0#1hello') {
if (input.value === 'hello#0#1') {
return;
}
@ -228,7 +228,7 @@ function next(msg) {
case 'input':
if (gFrameMsgCounts.input === 1) {
is(value, '#0hello',
is(value, 'hello#0',
'Failed to get correct input from the first iframe.');
} else {
ok(false, 'Unexpected multiple messages from input.')
@ -283,7 +283,7 @@ function next(msg) {
case 'input':
if (gFrameMsgCounts.input === 2) {
is(value, '#0#1hello',
is(value, 'hello#0#1',
'Failed to get correct input from the second iframe.');
} else {
ok(false, 'Unexpected multiple messages from input.')

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

@ -5657,8 +5657,8 @@ HTMLInputElement::SetRangeText(const nsAString& aReplacement, ErrorResult& aRv)
if (aRv.Failed()) {
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
start = state->GetSelectionProperties().mStart;
end = state->GetSelectionProperties().mEnd;
start = state->GetSelectionProperties().GetStart();
end = state->GetSelectionProperties().GetEnd();
aRv = NS_OK;
}
}
@ -5700,8 +5700,8 @@ HTMLInputElement::SetRangeText(const nsAString& aReplacement, uint32_t aStart,
if (aRv.Failed()) {
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
aSelectionStart = state->GetSelectionProperties().mStart;
aSelectionEnd = state->GetSelectionProperties().mEnd;
aSelectionStart = state->GetSelectionProperties().GetStart();
aSelectionEnd = state->GetSelectionProperties().GetEnd();
aRv = NS_OK;
}
}
@ -5770,7 +5770,7 @@ HTMLInputElement::GetSelectionStart(ErrorResult& aRv)
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
aRv = NS_OK;
return state->GetSelectionProperties().mStart;
return state->GetSelectionProperties().GetStart();
}
}
@ -5792,7 +5792,7 @@ HTMLInputElement::SetSelectionStart(int32_t aSelectionStart, ErrorResult& aRv)
{
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
state->GetSelectionProperties().mStart = aSelectionStart;
state->GetSelectionProperties().SetStart(aSelectionStart);
return;
}
@ -5834,7 +5834,7 @@ HTMLInputElement::GetSelectionEnd(ErrorResult& aRv)
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
aRv = NS_OK;
return state->GetSelectionProperties().mEnd;
return state->GetSelectionProperties().GetEnd();
}
}
@ -5856,7 +5856,7 @@ HTMLInputElement::SetSelectionEnd(int32_t aSelectionEnd, ErrorResult& aRv)
{
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
state->GetSelectionProperties().mEnd = aSelectionEnd;
state->GetSelectionProperties().SetEnd(aSelectionEnd);
return;
}
@ -5940,7 +5940,7 @@ HTMLInputElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aRv)
if (NS_FAILED(rv)) {
nsTextEditorState* state = GetEditorState();
if (state && state->IsSelectionCached()) {
DirectionToName(state->GetSelectionProperties().mDirection, aDirection);
DirectionToName(state->GetSelectionProperties().GetDirection(), aDirection);
return;
}
}
@ -5969,7 +5969,7 @@ HTMLInputElement::SetSelectionDirection(const nsAString& aDirection, ErrorResult
} else if (aDirection.EqualsLiteral("backward")) {
dir = nsITextControlFrame::eBackward;
}
state->GetSelectionProperties().mDirection = dir;
state->GetSelectionProperties().SetDirection(dir);
return;
}

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

@ -671,7 +671,7 @@ HTMLTextAreaElement::GetSelectionStart(ErrorResult& aError)
nsresult rv = GetSelectionRange(&selStart, &selEnd);
if (NS_FAILED(rv) && mState.IsSelectionCached()) {
return mState.GetSelectionProperties().mStart;
return mState.GetSelectionProperties().GetStart();
}
if (NS_FAILED(rv)) {
aError.Throw(rv);
@ -691,7 +691,7 @@ void
HTMLTextAreaElement::SetSelectionStart(uint32_t aSelectionStart, ErrorResult& aError)
{
if (mState.IsSelectionCached()) {
mState.GetSelectionProperties().mStart = aSelectionStart;
mState.GetSelectionProperties().SetStart(aSelectionStart);
return;
}
@ -734,7 +734,7 @@ HTMLTextAreaElement::GetSelectionEnd(ErrorResult& aError)
nsresult rv = GetSelectionRange(&selStart, &selEnd);
if (NS_FAILED(rv) && mState.IsSelectionCached()) {
return mState.GetSelectionProperties().mEnd;
return mState.GetSelectionProperties().GetEnd();
}
if (NS_FAILED(rv)) {
aError.Throw(rv);
@ -754,7 +754,7 @@ void
HTMLTextAreaElement::SetSelectionEnd(uint32_t aSelectionEnd, ErrorResult& aError)
{
if (mState.IsSelectionCached()) {
mState.GetSelectionProperties().mEnd = aSelectionEnd;
mState.GetSelectionProperties().SetEnd(aSelectionEnd);
return;
}
@ -831,7 +831,7 @@ HTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& a
if (NS_FAILED(rv)) {
if (mState.IsSelectionCached()) {
DirectionToName(mState.GetSelectionProperties().mDirection, aDirection);
DirectionToName(mState.GetSelectionProperties().GetDirection(), aDirection);
return;
}
aError.Throw(rv);
@ -856,7 +856,7 @@ HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection, ErrorRes
} else if (aDirection.EqualsLiteral("backward")) {
dir = nsITextControlFrame::eBackward;
}
mState.GetSelectionProperties().mDirection = dir;
mState.GetSelectionProperties().SetDirection(dir);
return;
}
@ -923,8 +923,8 @@ HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
aRv = GetSelectionRange(&start, &end);
if (aRv.Failed()) {
if (mState.IsSelectionCached()) {
start = mState.GetSelectionProperties().mStart;
end = mState.GetSelectionProperties().mEnd;
start = mState.GetSelectionProperties().GetStart();
end = mState.GetSelectionProperties().GetEnd();
aRv = NS_OK;
}
}
@ -961,8 +961,8 @@ HTMLTextAreaElement::SetRangeText(const nsAString& aReplacement,
aRv = GetSelectionRange(&aSelectionStart, &aSelectionEnd);
if (aRv.Failed()) {
if (mState.IsSelectionCached()) {
aSelectionStart = mState.GetSelectionProperties().mStart;
aSelectionEnd = mState.GetSelectionProperties().mEnd;
aSelectionStart = mState.GetSelectionProperties().GetStart();
aSelectionEnd = mState.GetSelectionProperties().GetEnd();
aRv = NS_OK;
}
}

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

@ -103,11 +103,13 @@ public:
// SetSelectionRange leads to Selection::AddRange which flushes Layout -
// need to block script to avoid nested PrepareEditor calls (bug 642800).
nsAutoScriptBlocker scriptBlocker;
nsTextEditorState::SelectionProperties& properties =
mTextEditorState->GetSelectionProperties();
mFrame->SetSelectionRange(properties.mStart,
properties.mEnd,
properties.mDirection);
nsTextEditorState::SelectionProperties& properties =
mTextEditorState->GetSelectionProperties();
if (properties.IsDirty()) {
mFrame->SetSelectionRange(properties.GetStart(),
properties.GetEnd(),
properties.GetDirection());
}
if (!mTextEditorState->mSelectionRestoreEagerInit) {
mTextEditorState->HideSelectionIfBlurred();
}
@ -1609,6 +1611,9 @@ nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
// side effect for unbinding from a text control frame, we need to call
// GetSelectionRange before calling DestroyEditor, and only if
// mEditorInitialized indicates that we actually have an editor available.
int32_t start = 0, end = 0;
nsITextControlFrame::SelectionDirection direction =
nsITextControlFrame::eForward;
if (mEditorInitialized) {
HTMLInputElement* number = GetParentNumberControl(aFrame);
if (number) {
@ -1616,13 +1621,16 @@ nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
// parent control, because this text editor state will be destroyed
// together with the native anonymous text control.
SelectionProperties props;
mBoundFrame->GetSelectionRange(&props.mStart, &props.mEnd,
&props.mDirection);
mBoundFrame->GetSelectionRange(&start, &end, &direction);
props.SetStart(start);
props.SetEnd(end);
props.SetDirection(direction);
number->SetSelectionProperties(props);
} else {
mBoundFrame->GetSelectionRange(&mSelectionProperties.mStart,
&mSelectionProperties.mEnd,
&mSelectionProperties.mDirection);
mBoundFrame->GetSelectionRange(&start, &end, &direction);
mSelectionProperties.SetStart(start);
mSelectionProperties.SetEnd(end);
mSelectionProperties.SetDirection(direction);
mSelectionCached = true;
}
}

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

@ -198,7 +198,7 @@ public:
bool GetPlaceholderVisibility() {
return mPlaceholderVisibility;
}
void UpdatePlaceholderText(bool aNotify);
void UpdatePlaceholderText(bool aNotify);
/**
* Get the maxlength attribute
@ -212,14 +212,50 @@ public:
void HideSelectionIfBlurred();
struct SelectionProperties {
SelectionProperties() : mStart(0), mEnd(0),
mDirection(nsITextControlFrame::eForward) {}
bool IsDefault() const {
return mStart == 0 && mEnd == 0 &&
mDirection == nsITextControlFrame::eForward;
}
int32_t mStart, mEnd;
nsITextControlFrame::SelectionDirection mDirection;
public:
SelectionProperties() : mStart(0), mEnd(0),
mDirection(nsITextControlFrame::eForward) {}
bool IsDefault() const
{
return mStart == 0 && mEnd == 0 &&
mDirection == nsITextControlFrame::eForward;
}
int32_t GetStart() const
{
return mStart;
}
void SetStart(int32_t value)
{
mIsDirty = true;
mStart = value;
}
int32_t GetEnd() const
{
return mEnd;
}
void SetEnd(int32_t value)
{
mIsDirty = true;
mEnd = value;
}
nsITextControlFrame::SelectionDirection GetDirection() const
{
return mDirection;
}
void SetDirection(nsITextControlFrame::SelectionDirection value)
{
mIsDirty = true;
mDirection = value;
}
// return true only if mStart, mEnd, or mDirection have been modified
bool IsDirty() const
{
return mIsDirty;
}
private:
int32_t mStart, mEnd;
bool mIsDirty = false;
nsITextControlFrame::SelectionDirection mDirection;
};
bool IsSelectionCached() const;

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

@ -51,8 +51,8 @@ function test(ctor) {
ok("selectionDirection" in elem, "elem should have the selectionDirection property");
is(elem.selectionStart, 0, "Default value");
is(elem.selectionEnd, 0, "Default value");
is(elem.selectionStart, elem.value.length, "Default value");
is(elem.selectionEnd, elem.value.length, "Default value");
is(elem.selectionDirection, "forward", "Default value");
var content = document.getElementById("content");
@ -70,8 +70,8 @@ function test(ctor) {
elem.value = "foobar";
is(elem.selectionStart, 0, "Default value");
is(elem.selectionEnd, 0, "Default value");
is(elem.selectionStart, elem.value.length, "Default value");
is(elem.selectionEnd, elem.value.length, "Default value");
is(elem.selectionDirection, "forward", "Default value");
elem.setSelectionRange(1, 3);

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

@ -11,7 +11,16 @@
}
</style>
</head>
<body onload="document.getElementById('i').focus();">
<body onload="focusInput();">
<script>
function focusInput() {
var inp = document.getElementById('i');
inp.selectionStart = 0;
inp.selectionEnd = 0;
inp.focus();
}
</script>
<a target="_blank" href="https://bugzil.la/1082486">Mozilla Bug 1082486</a>
<!-- The caret will not be seen when the input is focused. -->

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

@ -7,6 +7,8 @@
<script>
var textarea = document.querySelector("textarea");
function start() {
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
textarea.focus();
}
function done() {

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

@ -4,6 +4,8 @@
<script>
var textarea = document.querySelector("textarea");
function start() {
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
textarea.focus();
}
function done() {

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

@ -9,6 +9,8 @@
<script>
var textarea = document.querySelector("textarea");
function start() {
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
textarea.focus();
}
function done() {

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

@ -9,6 +9,8 @@ c</textarea>
<script>
var textarea = document.querySelector("textarea");
function start() {
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
textarea.focus();
}
function done() {

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

@ -303,9 +303,13 @@ nsTextControlFrame::EnsureEditorInitialized()
// editor.
mEditorHasBeenInitialized = true;
// Set the selection to the beginning of the text field.
nsAutoString val;
txtCtrl->GetTextEditorValue(val, true);
int32_t length = val.Length();
// Set the selection to the end of the text field. (bug 1287655)
if (weakFrame.IsAlive()) {
SetSelectionEndPoints(0, 0);
SetSelectionEndPoints(length, length);
}
}
NS_ENSURE_STATE(weakFrame.IsAlive());
@ -426,7 +430,7 @@ nsTextControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
nsIContent* placeholder = txtCtrl->GetPlaceholderNode();
if (placeholder && !(aFilter & nsIContent::eSkipPlaceholderContent))
aElements.AppendElement(placeholder);
}
nscoord
@ -927,18 +931,18 @@ nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart)
nsresult rv = EnsureEditorInitialized();
NS_ENSURE_SUCCESS(rv, rv);
int32_t selStart = 0, selEnd = 0;
int32_t selStart = 0, selEnd = 0;
rv = GetSelectionRange(&selStart, &selEnd);
NS_ENSURE_SUCCESS(rv, rv);
if (aSelectionStart > selEnd) {
// Collapse to the new start point.
selEnd = aSelectionStart;
selEnd = aSelectionStart;
}
selStart = aSelectionStart;
return SetSelectionEndPoints(selStart, selEnd);
}
@ -948,18 +952,18 @@ nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd)
nsresult rv = EnsureEditorInitialized();
NS_ENSURE_SUCCESS(rv, rv);
int32_t selStart = 0, selEnd = 0;
int32_t selStart = 0, selEnd = 0;
rv = GetSelectionRange(&selStart, &selEnd);
NS_ENSURE_SUCCESS(rv, rv);
if (aSelectionEnd < selStart) {
// Collapse to the new end point.
selStart = aSelectionEnd;
selStart = aSelectionEnd;
}
selEnd = aSelectionEnd;
return SetSelectionEndPoints(selStart, selEnd);
}
@ -1046,7 +1050,7 @@ nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart,
nsISelectionController* selCon = txtCtrl->GetSelectionController();
NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
nsCOMPtr<nsISelection> selection;
rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
@ -1185,7 +1189,7 @@ nsTextControlFrame::GetText(nsString& aText)
nsresult
nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
{
aPhonetic.Truncate(0);
aPhonetic.Truncate(0);
nsCOMPtr<nsIEditor> editor;
nsresult rv = GetEditor(getter_AddRefs(editor));

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

@ -18,13 +18,13 @@
doIs(t.value, "Test", "Shouldn't have lost our initial value");
t.focus();
sendString("Foo");
doIs(t.value, "FooTest", "Typing should work");
doIs(t.value, "TestFoo", "Typing should work");
window.parent.postMessage("c", "*");
} else {
doIs(evt.data, "continue", "Unexpected message");
doIs(t.value, "FooTest", "Shouldn't have lost our typed value");
doIs(t.value, "TestFoo", "Shouldn't have lost our typed value");
sendString("Bar");
doIs(t.value, "BarFooTest", "Typing should still work");
doIs(t.value, "TestFooBar", "Typing should still work");
window.parent.postMessage("f", "*");
}
},

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

@ -38,6 +38,8 @@ addLoadEvent(function() {
var area = document.getElementById("area");
is(area.scrollTop, 0, "The textarea should not be scrolled initially");
area.selectionStart = 0;
area.selectionEnd = 0;
area.focus();
setTimeout(function() {
is(area.scrollTop, 0, "The textarea's insertion point should not be scrolled into view");

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

@ -33,7 +33,7 @@ SimpleTest.waitForFocus(function() {
is(document.activeElement, i, "Should be focused before frame reconstruction");
synthesizeKey("1", {});
is(i.value, "1test", "Can accept keyboard events before frame reconstruction");
is(i.value, "test1", "Can accept keyboard events before frame reconstruction");
// force frame reconstruction
i.style.display = "none";
@ -43,7 +43,7 @@ SimpleTest.waitForFocus(function() {
is(document.activeElement, i, "Should be focused after frame reconstruction");
synthesizeKey("2", {});
is(i.value, "12test", "Can accept keyboard events after frame reconstruction");
is(i.value, "test12", "Can accept keyboard events after frame reconstruction");
// Make sure reframing happens gracefully
var reframeDiv = document.getElementById("reframe");

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

@ -80,9 +80,9 @@ function runTests(callback, type) {
d.focus();
is(d.value, "new", "Dynamic control's value can be set before initialization");
sendChar("x");
is(d.value, "xnew", "Dynamic control accepts keyboard input without explicit initialization");
is(d.value, "newx", "Dynamic control accepts keyboard input without explicit initialization");
$("display").removeChild(d);
is(d.value, "xnew", "Dynamic control retains value after being removed from the document");
is(d.value, "newx", "Dynamic control retains value after being removed from the document");
callback();
}

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

@ -12,8 +12,8 @@ abc
</textarea>
<div id="coords1">0</div>
<div id="coords2">0</div>
<div id="coords1">6</div>
<div id="coords2">6</div>
</body>
</html>

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

@ -1,18 +0,0 @@
[selection.html]
type: testharness
[test SelectionStart offset for input]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
[test SelectionStart offset for textarea]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
[test SelectionEnd offset for input]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655
[test SelectionEnd offset for textarea]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1287655

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

@ -632,7 +632,7 @@ add_task(function* test_form6_changeUsername() {
// Trigger the 'blur' event on uname
pword.focus();
yield spinEventLoop();
checkACForm("sXingleuser5", "singlepass5");
checkACForm("singleuser5X", "singlepass5");
setupScript.sendSyncMessage("removeLogin", "login5");
});

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

@ -98,6 +98,10 @@
let synthesizedKeys = [];
let expectations = [];
let textarea = document.getElementById("textarea");
textarea.selectionStart = 0;
textarea.selectionEnd = 0;
// Move to beginning of line
synthesizedKeys.push([KEYBOARD_LAYOUT_EN_US, MAC_VK_LeftArrow,
@ -341,4 +345,3 @@
</script>
</body>
</html>