Bug 1837126: Don't set the selection range when HyperTextAccessibleBase::PasteText is called with TEXT_OFFSET_CARET. r=eeejay

If no text is selected, this is the same as passing the caret offset, since the selection range is already where we want it.
However, if text is selected, this allows the caller to replace it, just as it would be when pasting using the keyboard or GUI.
Note that this doesn't break any existing expectations because TEXT_OFFSET_CARET was never supported for this method previously.

This allows us to remove the DeleteText call from Android SessionAccessibility::Paste.
This separate DeleteText call was causing problems because it could result in the deletion and the insertion happening in two separate ticks, resulting in incorrect events.
Now, this should always happen in a single tick.

Differential Revision: https://phabricator.services.mozilla.com/D226108
This commit is contained in:
James Teh 2024-10-20 22:35:34 +00:00
parent 5941f72bd3
commit d648744f28
4 changed files with 49 additions and 8 deletions

View File

@ -338,12 +338,7 @@ void SessionAccessibility::Copy(int32_t aID) {
void SessionAccessibility::Paste(int32_t aID) {
if (Accessible* acc = GetAccessibleByID(aID)) {
if (auto* textAcc = acc->AsHyperTextBase()) {
int32_t startSel, endSel;
GetSelectionOrCaret(textAcc, &startSel, &endSel);
if (startSel != endSel) {
textAcc->DeleteText(startSel, endSel);
}
textAcc->PasteText(startSel);
textAcc->PasteText(nsIAccessibleText::TEXT_OFFSET_CARET);
}
}
}

View File

@ -997,7 +997,13 @@ void HyperTextAccessible::DeleteText(int32_t aStartPos, int32_t aEndPos) {
void HyperTextAccessible::PasteText(int32_t aPosition) {
RefPtr<EditorBase> editorBase = GetEditor();
if (editorBase) {
SetSelectionRange(aPosition, aPosition);
// If the caller wants to paste at the caret, we don't need to set the
// selection. If there is text already selected, this also allows the caller
// to replace it, just as would happen when pasting using the keyboard or
// GUI.
if (aPosition != nsIAccessibleText::TEXT_OFFSET_CARET) {
SetSelectionRange(aPosition, aPosition);
}
editorBase->PasteAsAction(nsIClipboard::kGlobalClipboard,
EditorBase::DispatchPasteEvent::Yes);
}

View File

@ -50,7 +50,11 @@ interface nsIAccessibleEditableText : nsISupports
* Pastes text from the clipboard.
*
* @param position - index at which to insert the text from the system
* clipboard into the text represented by this object.
* clipboard into the text represented by this object. If
* this is TEXT_OFFSET_CARET, the text will be pasted at the
* caret just as it would be when pasting using the keyboard
* or GUI. This means that if text is already selected, it
* will be replaced with the pasted text.
*/
[can_run_script]
void pasteText(in long position);

View File

@ -171,3 +171,39 @@ addAccessibleTask(
contentDocBodyAttrs: { contentEditable: "true" },
}
);
/**
* Test PasteText replacement of selected text.
*/
addAccessibleTask(
`<input id="input" value="abcdef">`,
async function testPasteTextReplace(browser, docAcc) {
const input = findAccessibleChildByID(docAcc, "input");
let focused = waitForEvent(EVENT_FOCUS, input);
info("Focusing input");
input.takeFocus();
await focused;
info("Copying ef");
input.QueryInterface(nsIAccessibleEditableText);
let selected = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, input);
input.copyText(4, 6);
await selected;
info("Selecting bc");
selected = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, input);
await invokeContentTask(browser, [], () => {
const inputDom = content.document.getElementById("input");
inputDom.selectionStart = 1;
inputDom.selectionEnd = 3;
});
await selected;
info("Pasting at caret");
let changed = waitForEvents([
[EVENT_TEXT_REMOVED, input],
[EVENT_TEXT_INSERTED, input],
[EVENT_TEXT_VALUE_CHANGE, input],
]);
input.pasteText(nsIAccessibleText.TEXT_OFFSET_CARET);
await changed;
is(input.value, "aefdef", "input value correct after pasting");
}
);