Bug 998941 - part 1-2: Make editor set InputEvent.data to inserting text when it sets InputEvent.inputType to "insertText" or "insertCompositionText" r=smaug,m_kato

This patch makes nsContentUtils::DispatchInputEvent() support to set
InputEvent.data.  Whether the its value should be null or DOMString depends
on InputEvent.inputType value.

- https://rawgit.com/w3c/input-events/v1/index.html#overview
- https://rawgit.com/w3c/input-events/v1/index.html#dfn-data
- https://w3c.github.io/input-events/#overview
- https://w3c.github.io/input-events/#dfn-data

According to the draft specs, InputEvent.data should be always inserting text
when inputType is "insertText" or "insertCompositionText" (or
"insertFromCompoition" if Level 2 support is enabled).

Differential Revision: https://phabricator.services.mozilla.com/D19286

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-02-19 06:28:19 +00:00
Родитель 0f145fc8e5
Коммит a3484e40c0
19 изменённых файлов: 355 добавлений и 120 удалений

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

@ -4128,13 +4128,14 @@ nsresult nsContentUtils::DispatchEvent(Document* aDoc, nsISupports* aTarget,
nsresult nsContentUtils::DispatchInputEvent(Element* aEventTargetElement) {
RefPtr<TextEditor> textEditor; // See bug 1506439
return DispatchInputEvent(aEventTargetElement, EditorInputType::eUnknown,
textEditor);
textEditor, InputEventOptions());
}
// static
nsresult nsContentUtils::DispatchInputEvent(Element* aEventTargetElement,
EditorInputType aEditorInputType,
TextEditor* aTextEditor) {
TextEditor* aTextEditor,
const InputEventOptions& aOptions) {
if (NS_WARN_IF(!aEventTargetElement)) {
return NS_ERROR_INVALID_ARG;
}
@ -4231,6 +4232,31 @@ nsresult nsContentUtils::DispatchInputEvent(Element* aEventTargetElement,
inputEvent.mIsComposing =
aTextEditor ? !!aTextEditor->GetComposition() : false;
if (!aTextEditor || !aTextEditor->AsHTMLEditor()) {
if (IsDataAvailableOnTextEditor(aEditorInputType)) {
inputEvent.mData = aOptions.mData;
MOZ_ASSERT(!inputEvent.mData.IsVoid(),
"inputEvent.mData shouldn't be void");
}
#ifdef DEBUG
else {
MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
}
#endif // #ifdef DEBUG
} else {
MOZ_ASSERT(aTextEditor->AsHTMLEditor());
if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
inputEvent.mData = aOptions.mData;
MOZ_ASSERT(!inputEvent.mData.IsVoid(),
"inputEvent.mData shouldn't be void");
}
#ifdef DEBUG
else {
MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
}
#endif // #ifdef DEBUG
}
inputEvent.mInputType = aEditorInputType;
(new AsyncEventDispatcher(aEventTargetElement, inputEvent))

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

@ -1415,13 +1415,23 @@ class nsContentUtils {
* @param aTextEditor Optional. If this is called by editor,
* editor should set this. Otherwise, leave
* nullptr.
* @param aOptions Optional. If aEditorInputType value requires
* some additional data, they should be properly
* set with this argument.
*/
MOZ_CAN_RUN_SCRIPT
static nsresult DispatchInputEvent(Element* aEventTarget);
struct MOZ_STACK_CLASS InputEventOptions final {
InputEventOptions() = default;
explicit InputEventOptions(const nsAString& aData) : mData(aData) {}
nsString mData;
};
MOZ_CAN_RUN_SCRIPT
static nsresult DispatchInputEvent(Element* aEventTarget,
mozilla::EditorInputType aEditorInputType,
mozilla::TextEditor* aTextEditor);
mozilla::TextEditor* aTextEditor,
const InputEventOptions& aOptions);
/**
* This method creates and dispatches a untrusted event.

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

@ -59,7 +59,7 @@ function onunload()
SimpleTest.finish();
}
function checkInputEvent(aEvent, aIsComposing, aInputType, aDescription) {
function checkInputEvent(aEvent, aIsComposing, aInputType, aData, aDescription) {
if (aEvent.type != "input") {
return;
}
@ -68,6 +68,7 @@ function checkInputEvent(aEvent, aIsComposing, aInputType, aDescription) {
is(aEvent.bubbles, true, `${aDescription}"input" event should always bubble`);
is(aEvent.isComposing, aIsComposing, `${aDescription}isComposing should be ${aIsComposing}`);
is(aEvent.inputType, aInputType, `${aDescription}inputType should be "${aInputType}"`);
is(aEvent.data, aData, `${aDescription}data should be "${aData}"`);
}
const kIsMac = (navigator.platform.indexOf("Mac") == 0);
@ -233,7 +234,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be text");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], true, "insertCompositionText", description);
checkInputEvent(events[3], true, "insertCompositionText", composingStr, description);
TIP1.cancelComposition();
// Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during commitComposition().
@ -269,7 +270,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be compositionend");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertCompositionText", description);
checkInputEvent(events[2], false, "insertCompositionText", composingStr, description);
// Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during commitCompositionWith("bar").
events = [];
@ -317,7 +318,7 @@ function runBeginInputTransactionMethodTests()
description + "events[3] should be compositionend");
is(events[4].type, "input",
description + "events[4] should be input");
checkInputEvent(events[4], false, "insertCompositionText", description);
checkInputEvent(events[4], false, "insertCompositionText", "bar", description);
// Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during cancelComposition().
events = [];
@ -360,7 +361,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be compositionend");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], false, "insertCompositionText", description);
checkInputEvent(events[3], false, "insertCompositionText", "", description);
// Let's check if beginInputTransaction() fails to steal the rights of TextEventDispatcher during keydown() and keyup().
events = [];
@ -400,7 +401,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be keypress");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertText", description);
checkInputEvent(events[2], false, "insertText", "a", description);
is(events[3].type, "keyup",
description + "events[3] should be keyup");
@ -458,7 +459,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be text");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], true, "insertCompositionText", description);
checkInputEvent(events[3], true, "insertCompositionText", composingStr, description);
TIP1.cancelComposition();
// Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during commitComposition().
@ -494,7 +495,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be compositionend");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertCompositionText", description);
checkInputEvent(events[2], false, "insertCompositionText", composingStr, description);
// Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during commitCompositionWith("bar").
events = [];
@ -542,7 +543,7 @@ function runBeginInputTransactionMethodTests()
description + "events[3] should be compositionend");
is(events[4].type, "input",
description + "events[4] should be input");
checkInputEvent(events[4], false, "insertCompositionText", description);
checkInputEvent(events[4], false, "insertCompositionText", "bar", description);
// Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during cancelComposition().
events = [];
@ -585,7 +586,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be compositionend");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], false, "insertCompositionText", description);
checkInputEvent(events[3], false, "insertCompositionText", "", description);
// Let's check if beginInputTransactionForTests() fails to steal the rights of TextEventDispatcher during keydown() and keyup().
events = [];
@ -625,7 +626,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be keypress");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertText", description);
checkInputEvent(events[2], false, "insertText", "a", description);
is(events[3].type, "keyup",
description + "events[3] should be keyup");
@ -713,7 +714,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be text");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], true, "insertCompositionText", description);
checkInputEvent(events[3], true, "insertCompositionText", composingStr, description);
TIP1.cancelComposition();
// Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during commitComposition().
@ -767,7 +768,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be compositionend");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertCompositionText", description);
checkInputEvent(events[2], false, "insertCompositionText", composingStr, description);
// Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during commitCompositionWith("bar");.
events = [];
@ -845,7 +846,7 @@ function runBeginInputTransactionMethodTests()
description + "events[3] should be compositionend");
is(events[4].type, "input",
description + "events[4] should be input");
checkInputEvent(events[4], false, "insertCompositionText", description);
checkInputEvent(events[4], false, "insertCompositionText", "bar", description);
// Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during cancelComposition();.
events = [];
@ -912,7 +913,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be compositionend");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], false, "insertCompositionText", description);
checkInputEvent(events[3], false, "insertCompositionText", "", description);
// Let's check if beginInputTransaction() with another window fails to begin new input transaction with different TextEventDispatcher during keydown() and keyup();.
events = [];
@ -976,7 +977,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be keypress");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertText", description);
checkInputEvent(events[2], false, "insertText", "a", description);
is(events[3].type, "keyup",
description + "events[3] should be keyup");
@ -1064,7 +1065,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be text");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], true, "insertCompositionText", description);
checkInputEvent(events[3], true, "insertCompositionText", composingStr, description);
TIP1.cancelComposition();
// Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during commitComposition().
@ -1118,7 +1119,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be compositionend");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertCompositionText", description);
checkInputEvent(events[2], false, "insertCompositionText", composingStr, description);
// Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during commitCompositionWith("bar");.
events = [];
@ -1196,7 +1197,7 @@ function runBeginInputTransactionMethodTests()
description + "events[3] should be compositionend");
is(events[4].type, "input",
description + "events[4] should be input");
checkInputEvent(events[4], false, "insertCompositionText", description);
checkInputEvent(events[4], false, "insertCompositionText", "bar", description);
// Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during cancelComposition();.
events = [];
@ -1263,7 +1264,7 @@ function runBeginInputTransactionMethodTests()
description + "events[2] should be compositionend");
is(events[3].type, "input",
description + "events[3] should be input");
checkInputEvent(events[3], false, "insertCompositionText", description);
checkInputEvent(events[3], false, "insertCompositionText", "", description);
// Let's check if beginInputTransactionForTests() with another window fails to begin new input transaction with different TextEventDispatcher during keydown() and keyup();.
events = [];
@ -1327,7 +1328,7 @@ function runBeginInputTransactionMethodTests()
description + "events[1] should be keypress");
is(events[2].type, "input",
description + "events[2] should be input");
checkInputEvent(events[2], false, "insertText", description);
checkInputEvent(events[2], false, "insertText", "a", description);
is(events[3].type, "keyup",
description + "events[3] should be keyup");

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

@ -2434,7 +2434,8 @@ bool nsTextEditorState::SetValue(const nsAString& aValue,
MOZ_ASSERT(element);
RefPtr<TextEditor> textEditor; // See bug 1506439
DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(
element, EditorInputType::eInsertReplacementText, textEditor);
element, EditorInputType::eInsertReplacementText, textEditor,
nsContentUtils::InputEventOptions());
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Failed to dispatch input event");
}

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

@ -41,6 +41,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
let expectedInputType = "";
let expectedData = null;
function checkIfInputIsInputEvent(aEvent, aToDo, aDescription) {
if (aToDo) {
// Probably, key operation should fire "input" event with InputEvent interface.
@ -52,6 +53,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
`"input" event should be dispatched with InputEvent interface ${aDescription}`);
is(aEvent.inputType, expectedInputType,
`inputType should be "${expectedInputType}" ${aDescription}`);
is(aEvent.data, expectedData,
`data should be ${expectedData} ${aDescription}`);
}
is(aEvent.cancelable, false,
`"input" event should be never cancelable ${aDescription}`);
@ -132,17 +135,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
input = document.getElementById("input_" + textTypes[i]);
input.focus();
expectedInputType = "insertLineBreak";
expectedData = null;
synthesizeKey("KEY_Enter");
is(textInput[i], 0, "input event shouldn't be dispatched on " + textTypes[i] + " input element");
expectedInputType = "insertText";
expectedData = "m";
sendString("m");
is(textInput[i], 1, textTypes[i] + " input element should have dispatched input event.");
expectedInputType = "insertLineBreak";
expectedData = null;
synthesizeKey("KEY_Enter");
is(textInput[i], 1, "input event shouldn't be dispatched on " + textTypes[i] + " input element");
expectedInputType = "deleteContentBackward";
expectedData = null;
synthesizeKey("KEY_Backspace");
is(textInput[i], 2, textTypes[i] + " input element should have dispatched input event.");
}
@ -151,13 +158,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
input = document.getElementById("input_text");
input.focus();
expectedInputType = "insertText";
expectedData = "f";
sendString("f");
is(textInput[0], 3, "input event should have been dispatched");
input.blur();
is(textInput[0], 3, "input event should not have been dispatched");
input.focus();
expectedInputType = "insertText";
input.value = 'foo';
is(textInput[0], 3, "input event should not have been dispatched");
input.blur();
@ -165,6 +172,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
input.focus();
expectedInputType = "insertText";
expectedData = "f";
sendString("f");
is(textInput[0], 4, "input event should have been dispatched");
input.value = 'bar';
@ -176,6 +184,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
var textarea = document.getElementById("textarea");
textarea.focus();
expectedInputType = "insertText";
expectedData = "f";
sendString("f");
is(textareaInput, 1, "input event should have been dispatched");
textarea.blur();
@ -189,11 +198,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
textarea.focus();
expectedInputType = "insertText";
expectedData = "f";
sendString("f");
is(textareaInput, 2, "input event should have been dispatched");
textarea.value = 'bar';
is(textareaInput, 2, "input event should not have been dispatched");
expectedInputType = "deleteContentBackward";
expectedData = null;
synthesizeKey("KEY_Backspace");
is(textareaInput, 3, "input event should have been dispatched");
textarea.blur();
@ -277,6 +288,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
// <input type="number">'s inputType value hasn't been decided, see
// https://github.com/w3c/input-events/issues/88
expectedInputType = "";
expectedData = null;
synthesizeKey("KEY_ArrowUp");
is(numberInput, 1, "input event should be dispatched for up/down arrow key keypress");
is(number.value, "1", "sanity check value of number control after keypress");

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

@ -2030,7 +2030,8 @@ void EditorBase::NotifyEditorObservers(
}
}
void EditorBase::FireInputEvent(EditAction aEditAction) {
void EditorBase::FireInputEvent(EditAction aEditAction,
const nsAString& aData) {
MOZ_ASSERT(IsEditActionDataAvailable());
// We don't need to dispatch multiple input events if there is a pending
@ -2047,7 +2048,8 @@ void EditorBase::FireInputEvent(EditAction aEditAction) {
}
RefPtr<TextEditor> textEditor = AsTextEditor();
DebugOnly<nsresult> rvIgnored = nsContentUtils::DispatchInputEvent(
targetElement, ToInputType(aEditAction), textEditor);
targetElement, ToInputType(aEditAction), textEditor,
nsContentUtils::InputEventOptions(aData));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Failed to dispatch input event");
}
@ -4818,6 +4820,7 @@ EditorBase::AutoEditActionDataSetter::AutoEditActionDataSetter(
const EditorBase& aEditorBase, EditAction aEditAction)
: mEditorBase(const_cast<EditorBase&>(aEditorBase)),
mParentData(aEditorBase.mEditActionData),
mData(VoidString()),
mTopLevelEditSubAction(EditSubAction::eNone) {
// If we're nested edit action, copies necessary data from the parent.
if (mParentData) {

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

@ -649,6 +649,9 @@ class EditorBase : public nsIEditor,
const RefPtr<Selection>& SelectionRefPtr() const { return mSelection; }
EditAction GetEditAction() const { return mEditAction; }
void SetData(const nsAString& aData) { mData = aData; }
const nsString& GetData() const { return mData; }
void SetTopLevelEditSubAction(EditSubAction aEditSubAction,
EDirection aDirection = eNone) {
mTopLevelEditSubAction = aEditSubAction;
@ -756,6 +759,9 @@ class EditorBase : public nsIEditor,
// Utility class object for maintaining preserved ranges.
RangeUpdater mRangeUpdater;
// The data should be set to InputEvent.data.
nsString mData;
EditAction mEditAction;
EditSubAction mTopLevelEditSubAction;
EDirection mDirectionOfTopLevelEditSubAction;
@ -800,6 +806,14 @@ class EditorBase : public nsIEditor,
: EditAction::eNone;
}
/**
* GetInputEventData() returns inserting or inserted text value with
* current edit action. The result is proper for InputEvent.data value.
*/
const nsString& GetInputEventData() const {
return mEditActionData ? mEditActionData->GetData() : VoidString();
}
/**
* GetTopLevelEditSubAction() returns the top level edit sub-action.
* For example, if selected content is being replaced with inserted text,
@ -1786,9 +1800,11 @@ class EditorBase : public nsIEditor,
* asynchronously if it's not safe to dispatch.
*/
MOZ_CAN_RUN_SCRIPT
void FireInputEvent() { FireInputEvent(GetEditAction()); }
void FireInputEvent() {
FireInputEvent(GetEditAction(), GetInputEventData());
}
MOZ_CAN_RUN_SCRIPT
void FireInputEvent(EditAction aEditAction);
void FireInputEvent(EditAction aEditAction, const nsAString& aData);
/**
* Called after a transaction is done successfully.

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

@ -1814,6 +1814,8 @@ nsresult HTMLEditor::InsertTextWithQuotations(
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
MOZ_ASSERT(!aStringToInsert.IsVoid());
editActionData.SetData(aStringToInsert);
// The whole operation should be undoable in one transaction:
// XXX Why isn't enough to use only AutoPlaceholderBatch here?
@ -1924,6 +1926,8 @@ nsresult HTMLEditor::InsertAsQuotation(const nsAString& aQuotedText,
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
MOZ_ASSERT(!aQuotedText.IsVoid());
editActionData.SetData(aQuotedText);
AutoPlaceholderBatch treatAsOneTransaction(*this);
nsresult rv = InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -2112,6 +2116,8 @@ HTMLEditor::InsertAsCitedQuotation(const nsAString& aQuotedText,
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
MOZ_ASSERT(!aQuotedText.IsVoid());
editActionData.SetData(aQuotedText);
AutoPlaceholderBatch treatAsOneTransaction(*this);
nsresult rv = InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);

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

@ -406,6 +406,8 @@ nsresult TextEditor::OnInputText(const nsAString& aStringToInsert) {
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
MOZ_ASSERT(!aStringToInsert.IsVoid());
editActionData.SetData(aStringToInsert);
AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
nsresult rv = InsertTextAsSubAction(aStringToInsert);
@ -982,6 +984,10 @@ nsresult TextEditor::InsertTextAsAction(const nsAString& aStringToInsert) {
if (NS_WARN_IF(!editActionData.CanHandle())) {
return NS_ERROR_NOT_INITIALIZED;
}
// Note that we don't need to replace native line breaks with XP line breaks
// here because Chrome does not do it.
MOZ_ASSERT(!aStringToInsert.IsVoid());
editActionData.SetData(aStringToInsert);
AutoPlaceholderBatch treatAsOneTransaction(*this);
nsresult rv = InsertTextAsSubAction(aStringToInsert);
@ -1284,10 +1290,6 @@ nsresult TextEditor::OnCompositionChange(
return NS_ERROR_NOT_INITIALIZED;
}
if (!EnsureComposition(aCompositionChangeEvent)) {
return NS_OK;
}
// If:
// - new composition string is not empty,
// - there is no composition string in the DOM tree,
@ -1298,6 +1300,21 @@ nsresult TextEditor::OnCompositionChange(
editActionData.UpdateEditAction(EditAction::eDeleteByComposition);
}
// If Input Events Level 2 is enabled, EditAction::eDeleteByComposition is
// mapped to EditorInputType::eDeleteByComposition and it requires null
// for InputEvent.data. Therefore, only otherwise, we should set data.
if (ToInputType(editActionData.GetEditAction()) !=
EditorInputType::eDeleteByComposition) {
MOZ_ASSERT(ToInputType(editActionData.GetEditAction()) ==
EditorInputType::eInsertCompositionText);
MOZ_ASSERT(!aCompositionChangeEvent.mData.IsVoid());
editActionData.SetData(aCompositionChangeEvent.mData);
}
if (!EnsureComposition(aCompositionChangeEvent)) {
return NS_OK;
}
nsIPresShell* presShell = GetPresShell();
if (NS_WARN_IF(!presShell)) {
return NS_ERROR_NOT_INITIALIZED;
@ -1361,6 +1378,16 @@ void TextEditor::OnCompositionEnd(
if (NS_WARN_IF(!editActionData.CanHandle())) {
return;
}
// If Input Events Level 2 is enabled, EditAction::eCancelComposition is
// mapped to EditorInputType::eDeleteCompositionText and it requires null
// for InputEvent.data. Therefore, only otherwise, we should set data.
if (ToInputType(editAction) != EditorInputType::eDeleteCompositionText) {
MOZ_ASSERT(
ToInputType(editAction) == EditorInputType::eInsertCompositionText ||
ToInputType(editAction) == EditorInputType::eInsertFromComposition);
MOZ_ASSERT(!aCompositionEndEvent.mData.IsVoid());
editActionData.SetData(aCompositionEndEvent.mData);
}
// commit the IME transaction..we can get at it via the transaction mgr.
// Note that this means IME won't work without an undo stack!

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

@ -306,7 +306,7 @@ nsresult TextEditor::OnDrop(DragEvent* aDropEvent) {
// Let's fire "input" event for the deletion now.
if (mDispatchInputEvent) {
FireInputEvent(EditAction::eDeleteByDrag);
FireInputEvent(EditAction::eDeleteByDrag, VoidString());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}

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

@ -96,6 +96,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by 'a' key wasn't trusted event");
is(inputEvent.inputType, "insertText",
aDescription + 'inputType should be "insertText" when typing "a"');
is(inputEvent.data, "a",
aDescription + 'data should be "a" when typing "a"');
inputEvent = null;
synthesizeKey("KEY_Backspace", { }, aWindow);
@ -103,6 +105,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by BackSpace key wasn't trusted event");
is(inputEvent.inputType, "deleteContentBackward",
aDescription + 'inputType should be "deleteContentBackward" when pressing "Backspace" with collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Backspace" with collapsed selection');
inputEvent = null;
synthesizeKey("B", { shiftKey: true }, aWindow);
@ -110,6 +114,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by 'B' key wasn't trusted event");
is(inputEvent.inputType, "insertText",
aDescription + 'inputType should be "insertText" when typing "B"');
is(inputEvent.data, "B",
aDescription + 'data should be "B" when typing "B"');
inputEvent = null;
synthesizeKey("KEY_Enter", { }, aWindow);
@ -117,6 +123,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Enter key wasn't trusted event");
is(inputEvent.inputType, "insertParagraph",
aDescription + 'inputType should be "insertParagraph" when pressing "Enter"');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Enter"');
inputEvent = null;
synthesizeKey("C", { shiftKey: true }, aWindow);
@ -124,6 +132,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by 'C' key wasn't trusted event");
is(inputEvent.inputType, "insertText",
aDescription + 'inputType should be "insertText" when typing "C"');
is(inputEvent.data, "C",
aDescription + 'data should be "C" when typing "C"');
inputEvent = null;
synthesizeKey("KEY_Enter", { }, aWindow);
@ -131,6 +141,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Enter key (again) wasn't trusted event");
is(inputEvent.inputType, "insertParagraph",
aDescription + 'inputType should be "insertParagraph" when pressing "Enter" again');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Enter" again');
inputEvent = null;
editTarget.innerHTML = "foo-bar";
@ -146,6 +158,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Space key wasn't trusted event");
is(inputEvent.inputType, "insertText",
aDescription + 'inputType should be "insertText" when typing " "');
is(inputEvent.data, " ",
aDescription + 'data should be " " when typing " "');
inputEvent = null;
synthesizeKey("KEY_Delete", { }, aWindow);
@ -161,6 +175,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Delete key wasn't trusted event");
is(inputEvent.inputType, "deleteContentForward",
aDescription + 'inputType should be "deleteContentForward" when pressing "Delete" with collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Delete" with collapsed selection');
inputEvent = null;
synthesizeKey("z", { accelKey: true }, aWindow);
@ -168,6 +184,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Undo wasn't trusted event");
is(inputEvent.inputType, "historyUndo",
aDescription + 'inputType should be "historyUndo" when doing "Undo"');
is(inputEvent.data, null,
aDescription + 'data should be null when doing "Undo"');
inputEvent = null;
synthesizeKey("z", { accelKey: true, shiftKey: true }, aWindow);
@ -175,6 +193,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Redo wasn't trusted event");
is(inputEvent.inputType, "historyRedo",
aDescription + 'inputType should be "historyRedo" when doing "Redo"');
is(inputEvent.data, null,
aDescription + 'data should be null when doing "Redo"');
inputEvent = null;
synthesizeKey("KEY_Enter", {shiftKey: true}, aWindow);
@ -182,6 +202,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Shift + Enter key wasn't trusted event");
is(inputEvent.inputType, "insertLineBreak",
aDescription + 'inputType should be "insertLineBreak" when pressing Shift + "Enter"');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing Shift + "Enter"');
// Backspace/Delete with non-collapsed selection.
editTarget.innerHTML = "a";
@ -195,6 +217,8 @@ function runTests() {
aDescription + 'input event should be trusted when pressing "Backspace" with non-collapsed selection');
is(inputEvent.inputType, "deleteContentBackward",
aDescription + 'inputType should be "deleteContentBackward" when pressing "Backspace" with non-collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Backspace" with non-collapsed selection');
editTarget.innerHTML = "a";
editTarget.focus();
@ -207,6 +231,8 @@ function runTests() {
aDescription + 'input event should be trusted when pressing "Delete" with non-collapsed selection');
is(inputEvent.inputType, "deleteContentForward",
aDescription + 'inputType should be "deleteContentBackward" when Delete "Backspace" with non-collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when Delete "Backspace" with non-collapsed selection');
// Delete to previous/next word boundary with collapsed selection.
editTarget.innerHTML = "a";
@ -221,6 +247,8 @@ function runTests() {
aDescription + "input event should be trusted when deleting to previous word boundary with collapsed selection");
is(inputEvent.inputType, "deleteWordBackward",
aDescription + 'inputType should be "deleteWordBackward" when deleting to previous word boundary with collapsed selection');
is(inputEvent.data, null,
aDescription + "data should be null when deleting to previous word boundary with collapsed selection");
editTarget.innerHTML = "a";
editTarget.focus();
@ -234,6 +262,8 @@ function runTests() {
aDescription + "input event should be trusted when deleting to next word boundary with collapsed selection");
is(inputEvent.inputType, "deleteWordForward",
aDescription + 'inputType should be "deleteWordForward" when deleting to next word boundary with collapsed selection');
is(inputEvent.data, null,
aDescription + "data should be null when deleting to next word boundary with collapsed selection");
// Delete to previous/next word boundary with non-collapsed selection.
editTarget.innerHTML = "abc";
@ -253,6 +283,8 @@ function runTests() {
is(inputEvent.inputType, "deleteContentBackward",
aDescription + 'inputType should be "deleteContentBackward" when deleting to previous word boundary with non-collapsed selection');
}
is(inputEvent.data, null,
aDescription + "data should be null when deleting to previous word boundary with non-collapsed selection");
editTarget.innerHTML = "abc";
editTarget.focus();
@ -271,6 +303,8 @@ function runTests() {
is(inputEvent.inputType, "deleteContentForward",
aDescription + 'inputType should be "deleteContentForward" when deleting to next word boundary with non-collapsed selection');
}
is(inputEvent.data, null,
aDescription + "data should be null when deleting to next word boundary with non-collapsed selection");
// Delete to previous/next visual line boundary with collapsed selection.
editTarget.innerHTML = "a";
@ -285,6 +319,8 @@ function runTests() {
aDescription + "input event should be trusted when deleting to previous visual line boundary with collapsed selection");
is(inputEvent.inputType, "deleteSoftLineBackward",
aDescription + 'inputType should be "deleteSoftLineBackward" when deleting to previous visual line boundary with collapsed selection');
is(inputEvent.data, null,
aDescription + "data should be null when deleting to previous visual line boundary with collapsed selection");
editTarget.innerHTML = "a";
editTarget.focus();
@ -298,6 +334,8 @@ function runTests() {
aDescription + "input event should be trusted when deleting to next visual line boundary with collapsed selection");
is(inputEvent.inputType, "deleteSoftLineForward",
aDescription + 'inputType should be "deleteSoftLineForward" when deleting to visual line boundary with collapsed selection');
is(inputEvent.data, null,
aDescription + "data should be null when deleting to visual line boundary with collapsed selection");
// Delete to previous/next visual line boundary with non-collapsed selection.
editTarget.innerHTML = "abc";
@ -317,6 +355,8 @@ function runTests() {
is(inputEvent.inputType, "deleteContentBackward",
aDescription + 'inputType should be "deleteContentBackward" when deleting to previous visual line boundary with non-collapsed selection');
}
is(inputEvent.data, null,
aDescription + "data should be null when deleting to previous visual line boundary with non-collapsed selection");
editTarget.innerHTML = "abc";
editTarget.focus();
@ -335,6 +375,8 @@ function runTests() {
is(inputEvent.inputType, "deleteContentForward",
aDescription + 'inputType should be "deleteContentForward" when deleting to next visual line boundary with non-collapsed selection');
}
is(inputEvent.data, null,
aDescription + "data should be null when deleting to next visual line boundary with non-collapsed selection");
aWindow.removeEventListener("input", handler, true);
}

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

@ -68,6 +68,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by 'a' key wasn't trusted event");
is(inputEvent.inputType, "insertText",
aDescription + 'inputType should be "insertText" when typing "a"');
is(inputEvent.data, "a",
aDescription + 'data should be "a" when typing "a"');
inputEvent = null;
synthesizeKey("KEY_Backspace");
@ -76,6 +78,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by BackSpace key wasn't trusted event");
is(inputEvent.inputType, "deleteContentBackward",
aDescription + 'inputType should be "deleteContentBackward" when pressing "Backspace" with collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Backspace" with collapsed selection');
if (aIsTextarea) {
inputEvent = null;
@ -85,6 +89,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Enter key wasn't trusted event");
is(inputEvent.inputType, "insertLineBreak",
aDescription + 'inputType should be "insertLineBreak" when pressing "Enter"');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Enter"');
}
inputEvent = null;
@ -104,6 +110,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Space key wasn't trusted event");
is(inputEvent.inputType, "insertText",
aDescription + 'inputType should be "insertText" when typing " "');
is(inputEvent.data, " ",
aDescription + 'data should be " " when typing " "');
inputEvent = null;
synthesizeKey("KEY_Delete");
@ -122,6 +130,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Delete key wasn't trusted event");
is(inputEvent.inputType, "deleteContentForward",
aDescription + 'inputType should be "deleteContentForward" when pressing "Delete" with collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Delete" with collapsed selection');
inputEvent = null;
synthesizeKey("z", {accelKey: true});
@ -130,6 +140,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Undo wasn't trusted event");
is(inputEvent.inputType, "historyUndo",
aDescription + 'inputType should be "historyUndo" when doing "Undo"');
is(inputEvent.data, null,
aDescription + 'data should be null when doing "Undo"');
inputEvent = null;
synthesizeKey("Z", {accelKey: true, shiftKey: true});
@ -138,6 +150,8 @@ function runTests() {
ok(inputEvent.isTrusted, aDescription + "input event by Redo wasn't trusted event");
is(inputEvent.inputType, "historyRedo",
aDescription + 'inputType should be "historyRedo" when doing "Redo"');
is(inputEvent.data, null,
aDescription + 'data should be null when doing "Redo"');
// Backspace/Delete with non-collapsed selection.
aElement.value = "a";
@ -150,6 +164,8 @@ function runTests() {
aDescription + 'input event should be trusted when pressing "Backspace" with non-collapsed selection');
is(inputEvent.inputType, "deleteContentBackward",
aDescription + 'inputType should be "deleteContentBackward" when pressing "Backspace" with non-collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when pressing "Backspace" with non-collapsed selection');
aElement.value = "a";
aElement.select();
@ -161,6 +177,8 @@ function runTests() {
aDescription + 'input event should be trusted when pressing "Delete" with non-collapsed selection');
is(inputEvent.inputType, "deleteContentForward",
aDescription + 'inputType should be "deleteContentBackward" when Delete "Backspace" with non-collapsed selection');
is(inputEvent.data, null,
aDescription + 'data should be null when Delete "Backspace" with non-collapsed selection');
aElement.removeEventListener("input", handler, true);
}

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

@ -53,6 +53,8 @@ SimpleTest.waitForFocus(function() {
'"input" event should always bubble after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor');
is(inputEvents[0].inputType, "insertText",
'inputType should be "insertText" after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor');
is(inputEvents[0].data, "this is quoted text\nAnd here is second line.",
"data should be the quoted text after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor");
// Tests when the editor is in HTML editor mode.
getEditor().flags &= ~SpecialPowers.Ci.nsIPlaintextEditor.eEditorPlaintextMask;
@ -80,6 +82,8 @@ SimpleTest.waitForFocus(function() {
'"input" event should always bubble after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext)');
is(inputEvents[0].inputType, "",
"inputType should be empty string after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext)");
is(inputEvents[0].data, null,
"data should be null after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext)");
editor.innerHTML = "";
@ -104,6 +108,8 @@ SimpleTest.waitForFocus(function() {
'"input" event should always bubble after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source)');
is(inputEvents[0].inputType, "",
"inputType should be empty string after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source)");
is(inputEvents[0].data, null,
"data should be null after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source)");
SimpleTest.finish();
});

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

@ -29,7 +29,7 @@ SimpleTest.waitForFocus(function() {
document.getElementById("textarea"),
];
for (let editableElement of editableElements) {
function checkInputEvent(aEvent, aInputType, aDescription) {
function checkInputEvent(aEvent, aInputType, aData, aDescription) {
ok(aEvent instanceof InputEvent,
`"input" event should be dispatched with InputEvent interface ${aDescription}`);
is(aEvent.cancelable, false,
@ -38,6 +38,8 @@ SimpleTest.waitForFocus(function() {
`"input" event should always bubble ${aDescription}`);
is(aEvent.inputType, aInputType,
`inputType should be "${aInputType}" ${aDescription}`);
is(aEvent.data, aData,
`data should be ${aData} ${aDescription}`);
}
let inputEvents = [];
@ -52,13 +54,13 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("a");
is(inputEvents.length, 1,
`Only one "input" event should be fired when inserting "a" with key on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", `when inserting "a" with key on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", "a", `when inserting "a" with key on <${editableElement.tagName.toLowerCase()}> element`);
inputEvents = [];
synthesizeKey("c");
is(inputEvents.length, 1,
`Only one "input" event should be fired when inserting "c" with key on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", `when inserting "c" with key on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", "c", `when inserting "c" with key on <${editableElement.tagName.toLowerCase()}> element`);
inputEvents = [];
synthesizeKey("KEY_ArrowLeft");
@ -69,7 +71,7 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("b");
is(inputEvents.length, 1,
`Only one "input" event should be fired when inserting "b" with key on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", `when inserting "b" with key on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", "b", `when inserting "b" with key on <${editableElement.tagName.toLowerCase()}> element`);
let editor = SpecialPowers.wrap(editableElement).editor;
let transactionManager = editor.transactionManager;
@ -93,7 +95,7 @@ SimpleTest.waitForFocus(function() {
synthesizeKey("a");
is(inputEvents.length, 1,
`Only one "input" event should be fired when inserting "a" with key again on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", `when inserting "a" with key again on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "insertText", "a", `when inserting "a" with key again on <${editableElement.tagName.toLowerCase()}> element`);
inputEvents = [];
synthesizeKey("z", { accelKey: true });
@ -101,7 +103,7 @@ SimpleTest.waitForFocus(function() {
editableElement.tagName + ": undo should work after setting value");
is(inputEvents.length, 1,
`Only one "input" event should be fired when undoing on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "historyUndo", `when undoing on <${editableElement.tagName.toLowerCase()}> element`);
checkInputEvent(inputEvents[0], "historyUndo", null, `when undoing on <${editableElement.tagName.toLowerCase()}> element`);
// Disable undo/redo.
editor.enableUndo(0);

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

@ -96,6 +96,10 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
this._description + ", " + aTest.description + ': "input" event should be never cancelable');
this._is(aInputEvents[i].bubbles, true,
this._description + ", " + aTest.description + ': "input" event should always bubble');
this._is(aInputEvents[i].inputType, aTest.inputEvents[i].inputType,
this._description + ", " + aTest.description + ': inputType of "input" event should be "${aTest.inputEvents[i].inputType}"');
this._is(aInputEvents[i].data, aTest.inputEvents[i].data,
this._description + ", " + aTest.description + ': data of "input" event should be ${aTest.inputEvents[i].data}');
}
},
@ -108,8 +112,8 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mo", searchString: "Mo",
inputEvents: [
{inputType: "insertText"},
{inputType: "insertText"},
{inputType: "insertText", data: "M"},
{inputType: "insertText", data: "o"},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case: select 'Mozilla' to complete the word",
@ -120,7 +124,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "Mozilla", searchString: "Mozilla",
inputEvents: [
{inputType: "insertReplacementText"},
{inputType: "insertReplacementText", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case: undo the word, but typed text shouldn't be canceled",
@ -130,7 +134,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mo", searchString: "Mo",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case: undo the typed text",
@ -140,7 +144,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case: redo the typed text",
@ -150,7 +154,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mo", searchString: "Mo",
inputEvents: [
{inputType: "historyRedo"},
{inputType: "historyRedo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case: redo the word",
@ -160,7 +164,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mozilla", searchString: "Mozilla",
inputEvents: [
{inputType: "historyRedo"},
{inputType: "historyRedo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case: removing all text for next test...",
@ -171,7 +175,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "deleteContentBackward"},
{inputType: "deleteContentBackward", data: null},
],
},
@ -183,8 +187,8 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mo", searchString: "mo",
inputEvents: [
{inputType: "insertText"},
{inputType: "insertText"},
{inputType: "insertText", data: "m"},
{inputType: "insertText", data: "o"},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case: select 'Mozilla' to complete the word",
@ -195,7 +199,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "Mozilla", searchString: "Mozilla",
inputEvents: [
{inputType: "insertReplacementText"},
{inputType: "insertReplacementText", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case: undo the word, but typed text shouldn't be canceled",
@ -205,7 +209,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mo", searchString: "mo",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case: undo the typed text",
@ -215,7 +219,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case: redo the typed text",
@ -225,7 +229,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mo", searchString: "mo",
inputEvents: [
{inputType: "historyRedo"},
{inputType: "historyRedo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case: redo the word",
@ -235,7 +239,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mozilla", searchString: "Mozilla",
inputEvents: [
{inputType: "historyRedo"},
{inputType: "historyRedo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case: removing all text for next test...",
@ -246,7 +250,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "deleteContentBackward"},
{inputType: "deleteContentBackward", data: null},
],
},
@ -263,9 +267,9 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mozilla", searchString: "Mo",
inputEvents: [
{inputType: "insertText"},
{inputType: "insertText"},
{inputType: "insertReplacementText"},
{inputType: "insertText", data: "M"},
{inputType: "insertText", data: "o"},
{inputType: "insertReplacementText", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case (completeDefaultIndex is true): select 'Mozilla' to complete the word",
@ -293,7 +297,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mo", searchString: "Mo",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case (completeDefaultIndex is true): undo the typed text",
@ -307,7 +311,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case (completeDefaultIndex is true): redo the typed text",
@ -321,8 +325,8 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "Mozilla", searchString: "Mo",
inputEvents: [
{inputType: "historyRedo"},
{inputType: "insertReplacementText"},
{inputType: "historyRedo", data: null},
{inputType: "insertReplacementText", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text exactly matches the case (completeDefaultIndex is true): redo the word",
@ -350,7 +354,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "deleteContentBackward"},
{inputType: "deleteContentBackward", data: null},
],
},
@ -366,9 +370,9 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mozilla", searchString: "mo",
inputEvents: [
{inputType: "insertText"},
{inputType: "insertText"},
{inputType: "insertReplacementText"},
{inputType: "insertText", data: "m"},
{inputType: "insertText", data: "o"},
{inputType: "insertReplacementText", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case (completeDefaultIndex is true): select 'Mozilla' to complete the word",
@ -383,7 +387,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "Mozilla", searchString: "Mozilla",
inputEvents: [
{inputType: "insertReplacementText"},
{inputType: "insertReplacementText", data: null},
],
},
// Different from "exactly matches the case" case, modifying the case causes one additional transaction.
@ -399,7 +403,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mozilla", searchString: "mozilla",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case (completeDefaultIndex is true): undo the word, but typed text shouldn't be canceled",
@ -413,7 +417,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mo", searchString: "mo",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case (completeDefaultIndex is true): undo the typed text",
@ -427,7 +431,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "historyUndo"},
{inputType: "historyUndo", data: null},
],
},
// XXX This is odd case. Consistency with undo behavior, this should restore "mo".
@ -445,8 +449,8 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: true, value: "mozilla", searchString: "mo",
inputEvents: [
{inputType: "historyRedo"},
{inputType: "insertReplacementText"},
{inputType: "historyRedo", data: null},
{inputType: "insertReplacementText", data: null},
],
},
{ description: "Undo/Redo behavior check when typed text does not match the case (completeDefaultIndex is true): redo the default index word",
@ -487,7 +491,7 @@ nsDoTestsForEditorWithAutoComplete.prototype = {
return true;
}, popup: false, value: "", searchString: "",
inputEvents: [
{inputType: "deleteContentBackward"},
{inputType: "deleteContentBackward", data: null},
],
},
],

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

@ -130,6 +130,36 @@ enum class EditorInputType : EditorInputTypeType {
#undef NS_DEFINE_INPUTTYPE
/**
* IsDataAvailableOnTextEditor() returns true if aInputType on TextEditor
* should have non-null InputEvent.data value.
*/
inline bool IsDataAvailableOnTextEditor(EditorInputType aInputType) {
switch (aInputType) {
case EditorInputType::eInsertText:
case EditorInputType::eInsertCompositionText:
case EditorInputType::eInsertFromComposition: // Only level 2
return true;
default:
return false;
}
}
/**
* IsDataAvailableOnHTMLEditor() returns true if aInputType on HTMLEditor
* should have non-null InputEvent.data value.
*/
inline bool IsDataAvailableOnHTMLEditor(EditorInputType aInputType) {
switch (aInputType) {
case EditorInputType::eInsertText:
case EditorInputType::eInsertCompositionText:
case EditorInputType::eInsertFromComposition: // Only level 2
return true;
default:
return false;
}
}
#define NS_DEFINE_COMMAND(aName, aCommandStr) , Command##aName
#define NS_DEFINE_COMMAND_NO_EXEC_COMMAND(aName) , Command##aName

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

@ -385,6 +385,9 @@ nsresult TextEventDispatcher::CommitComposition(
}
if (message == eCompositionCommit) {
compositionCommitEvent.mData = *aCommitString;
// If aCommitString comes from TextInputProcessor, it may be void, but
// editor requires non-void string even when it's empty.
compositionCommitEvent.mData.SetIsVoid(false);
// Don't send CRLF nor CR, replace it with LF here.
compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
NS_LITERAL_STRING("\n"));
@ -908,6 +911,9 @@ nsresult TextEventDispatcher::PendingComposition::Flush(
compChangeEvent.AssignEventTime(*aEventTime);
}
compChangeEvent.mData = mString;
// If mString comes from TextInputProcessor, it may be void, but editor
// requires non-void string even when it's empty.
compChangeEvent.mData.SetIsVoid(false);
if (mClauses) {
MOZ_ASSERT(!mClauses->IsEmpty(),
"mClauses must be non-empty array when it's not nullptr");

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

@ -1140,7 +1140,9 @@ class WidgetSelectionEvent : public WidgetGUIEvent {
class InternalEditorInputEvent : public InternalUIEvent {
private:
InternalEditorInputEvent()
: mInputType(EditorInputType::eUnknown), mIsComposing(false) {}
: mData(VoidString()),
mInputType(EditorInputType::eUnknown),
mIsComposing(false) {}
public:
virtual InternalEditorInputEvent* AsEditorInputEvent() override {
@ -1150,6 +1152,7 @@ class InternalEditorInputEvent : public InternalUIEvent {
InternalEditorInputEvent(bool aIsTrusted, EventMessage aMessage,
nsIWidget* aWidget = nullptr)
: InternalUIEvent(aIsTrusted, aMessage, aWidget, eEditorInputEventClass),
mData(VoidString()),
mInputType(EditorInputType::eUnknown) {}
virtual WidgetEvent* Duplicate() const override {

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

@ -643,7 +643,7 @@ function runUndoRedoTest()
}
}
function checkInputEvent(aEvent, aIsComposing, aInputType, aDescription) {
function checkInputEvent(aEvent, aIsComposing, aInputType, aData, aDescription) {
if (aEvent.type != "input") {
return;
}
@ -652,6 +652,7 @@ function checkInputEvent(aEvent, aIsComposing, aInputType, aDescription) {
is(aEvent.bubbles, true, `"input" event should always bubble: ${aDescription}`);
is(aEvent.isComposing, aIsComposing, `isComposing should be ${aIsComposing}: ${aDescription}`);
is(aEvent.inputType, aInputType, `inputType should be "${aInputType}": ${aDescription}`);
is(aEvent.data, aData, `data should be ${aData}: ${aDescription}`);
}
function runCompositionCommitAsIsTest()
@ -700,7 +701,7 @@ function runCompositionCommitAsIsTest()
"runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #1");
is(result[2].type, "input",
"runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #1");
checkInputEvent(result[2], false, "insertCompositionText",
checkInputEvent(result[2], false, "insertCompositionText", "\u3042",
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1");
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #1");
@ -740,7 +741,7 @@ function runCompositionCommitAsIsTest()
"runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #2");
is(result[1].type, "input",
"runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #2");
checkInputEvent(result[1], false, "insertCompositionText",
checkInputEvent(result[1], false, "insertCompositionText", "\u3042",
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #2");
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2");
@ -780,7 +781,7 @@ function runCompositionCommitAsIsTest()
"runCompositionCommitAsIsTest: compositionend shouldn't be fired after dispatching compositioncommitasis #3");
is(result[1].type, "input",
"runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #3");
checkInputEvent(result[1], false, "insertCompositionText",
checkInputEvent(result[1], false, "insertCompositionText", "",
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #3");
is(textarea.value, "", "runCompositionCommitAsIsTest: textarea doesn't have committed string #3");
@ -838,7 +839,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #1");
is(result[3].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #1");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "\u3043",
"runCompositionCommitTest: after dispatching compositioncommit #1");
is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #1");
@ -882,7 +883,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #2");
is(result[3].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #2");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "\u3043",
"runCompositionCommitTest: after dispatching compositioncommit #2");
is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #2");
@ -926,7 +927,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #3");
is(result[3].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #3");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "\u3043",
"runCompositionCommitTest: after dispatching compositioncommit #3");
is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #3");
@ -946,7 +947,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired when inserting empty string with composition");
is(result[2].type, "input",
"runCompositionCommitTest: input should be fired when inserting empty string with composition");
checkInputEvent(result[2], false, "insertCompositionText",
checkInputEvent(result[2], false, "insertCompositionText", "",
"runCompositionCommitTest: when inserting empty string with composition");
is(textarea.value, "abc",
"runCompositionCommitTest: textarea should keep original value when inserting empty string with composition");
@ -967,7 +968,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired when replacing with empty string with composition");
is(result[2].type, "input",
"runCompositionCommitTest: input should be fired when replacing with empty string with composition");
checkInputEvent(result[2], false, "insertCompositionText",
checkInputEvent(result[2], false, "insertCompositionText", "",
"runCompositionCommitTest: when replacing with empty string with composition");
is(textarea.value, "",
"runCompositionCommitTest: textarea should become empty when replacing selection with empty string with composition");
@ -990,7 +991,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired when replacing selection with same string with composition");
is(result[3].type, "input",
"runCompositionCommitTest: input should be fired when replacing selection with same string with composition");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "abc",
"runCompositionCommitTest: when replacing selection with same string with composition");
is(textarea.value, "abc",
"runCompositionCommitTest: textarea should keep same value when replacing selection with same string with composition");
@ -1023,7 +1024,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #4");
is(result[3].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #4");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "",
"runCompositionCommitTest: after dispatching compositioncommit #4");
is(textarea.value, "", "runCompositionCommitTest: textarea should be empty #4");
@ -1043,7 +1044,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
is(result[3].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #5");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "\u3042",
"runCompositionCommitTest: after dispatching compositioncommit #5");
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should be empty #5");
@ -1073,7 +1074,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #6");
is(result[2].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #6");
checkInputEvent(result[2], false, "insertCompositionText",
checkInputEvent(result[2], false, "insertCompositionText", "\u3042",
"runCompositionCommitTest: after dispatching compositioncommit #6");
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #6");
@ -1114,7 +1115,7 @@ function runCompositionCommitTest()
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #7");
is(result[1].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #7");
checkInputEvent(result[1], false, "insertCompositionText",
checkInputEvent(result[1], false, "insertCompositionText", "\u3042",
"runCompositionCommitTest: after dispatching compositioncommit #7");
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #6");
@ -5055,7 +5056,8 @@ function runForceCommitTest()
"runForceCommitTest: the 3rd event must be text #1");
is(events[3].type, "input",
"runForceCommitTest: the 4th event must be input #1");
checkInputEvent(events[3], true, "insertCompositionText", "runForceCommitTest #1");
checkInputEvent(events[3], true, "insertCompositionText", "\u306E",
"runForceCommitTest #1");
events = [];
synthesizeMouseAtCenter(textarea, {});
@ -5068,7 +5070,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #2");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #2");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #2");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #2");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #2");
is(events[0].target, textarea,
@ -5109,7 +5112,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #3");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #3");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #3");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #3");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #3");
is(events[0].target, textarea,
@ -5153,7 +5157,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #4");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #4");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #4");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #4");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #4");
is(events[0].target, textarea,
@ -5194,7 +5199,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #5");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #5");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #5");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #5");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #5");
is(events[0].target, textarea,
@ -5239,7 +5245,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #6");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #6");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #6");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #6");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #6");
is(events[0].target, textarea,
@ -5284,7 +5291,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #7");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #7");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #7");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #7");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #7");
is(events[0].target, textarea,
@ -5330,7 +5338,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #8");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #8");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #8");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #8");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #8");
is(events[0].target, textarea,
@ -5375,7 +5384,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #9");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #9");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #9");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #9");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #9");
is(events[0].target, iframe2.contentDocument.body,
@ -5418,7 +5428,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #10");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #10");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #10");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #10");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #10");
is(events[0].target, iframe2.contentDocument.body,
@ -5466,7 +5477,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #11");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #11");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #11");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #11");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #11");
is(events[0].target, iframe2.contentDocument.body,
@ -5510,7 +5522,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #12");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #12");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #12");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #12");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #12");
is(events[0].target, input,
@ -5549,7 +5562,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #13");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #13");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #13");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #13");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #13");
is(events[0].target, textarea,
@ -5588,7 +5602,8 @@ function runForceCommitTest()
"runForceCommitTest: the 2nd event must be compositionend #14");
is(events[2].type, "input",
"runForceCommitTest: the 3rd event must be input #14");
checkInputEvent(events[2], false, "insertCompositionText", "runForceCommitTest #14");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runForceCommitTest #14");
is(events[1].data, "\u306E",
"runForceCommitTest: compositionend has wrong data #14");
is(events[0].target, input,
@ -5701,7 +5716,8 @@ function runNestedSettingValue()
"runNestedSettingValue: the 2nd event must be compositionend #1");
is(events[2].type, "input",
"runNestedSettingValue: the 3rd event must be input #1");
checkInputEvent(events[2], false, "insertCompositionText", "runNestedSettingValue #1");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runNestedSettingValue #1");
is(events[1].data, "\u306E",
"runNestedSettingValue: compositionend has wrong data #1");
is(events[0].target, textarea,
@ -5742,7 +5758,8 @@ function runNestedSettingValue()
"runNestedSettingValue: the 2nd event must be compositionend #2");
is(events[2].type, "input",
"runNestedSettingValue: the 3rd event must be input #2");
checkInputEvent(events[2], false, "insertCompositionText", "runNestedSettingValue #2");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runNestedSettingValue #2");
is(events[1].data, "\u306E",
"runNestedSettingValue: compositionend has wrong data #2");
is(events[0].target, input,
@ -5783,7 +5800,8 @@ function runNestedSettingValue()
"runNestedSettingValue: the 2nd event must be compositionend #3");
is(events[2].type, "input",
"runNestedSettingValue: the 3rd event must be input #3");
checkInputEvent(events[2], false, "insertCompositionText", "runNestedSettingValue #3");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runNestedSettingValue #3");
is(events[1].data, "\u306E",
"runNestedSettingValue: compositionend has wrong data #3");
is(events[0].target, textarea,
@ -5824,7 +5842,8 @@ function runNestedSettingValue()
"runNestedSettingValue: the 2nd event must be compositionend #4");
is(events[2].type, "input",
"runNestedSettingValue: the 3rd event must be input #4");
checkInputEvent(events[2], false, "insertCompositionText", "runNestedSettingValue #4");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runNestedSettingValue #4");
is(events[1].data, "\u306E",
"runNestedSettingValue: compositionend has wrong data #4");
is(events[0].target, input,
@ -5917,7 +5936,8 @@ function runAsyncForceCommitTest()
"runAsyncForceCommitTest: the 3rd event must be text #1");
is(events[3].type, "input",
"runAsyncForceCommitTest: the 4th event must be input #1");
checkInputEvent(events[3], true, "insertCompositionText", "runAsyncForceCommitTest #1");
checkInputEvent(events[3], true, "insertCompositionText", "\u306E",
"runAsyncForceCommitTest #1");
events = [];
commitRequested = false;
@ -5933,7 +5953,8 @@ function runAsyncForceCommitTest()
"runAsyncForceCommitTest: the 2nd event must be compositionend #2");
is(events[2].type, "input",
"runAsyncForceCommitTest: the 3rd event must be input #2");
checkInputEvent(events[2], false, "insertCompositionText", "runAsyncForceCommitTest #2");
checkInputEvent(events[2], false, "insertCompositionText", "\u306E",
"runAsyncForceCommitTest #2");
is(events[1].data, "\u306E",
"runAsyncForceCommitTest: compositionend has wrong data #2");
is(events[0].target, textarea,
@ -5985,7 +6006,8 @@ function runIsComposingTest()
is(aEvent.isComposing, false,
"runIsComposingTest: " + description + " (type=" + aEvent.type + ")");
} else {
checkInputEvent(aEvent, expectedIsComposing, "insertCompositionText", `runIsComposingTest: ${description}`);
checkInputEvent(aEvent, expectedIsComposing, "insertCompositionText", "\u3042",
`runIsComposingTest: ${description}`);
}
}
@ -6095,7 +6117,7 @@ function runRedundantChangeTest()
"runRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string #1");
is(result[2].type, "input",
"runRedundantChangeTest: input should be fired after synthesizing composition change #1");
checkInputEvent(result[2], true, "insertCompositionText",
checkInputEvent(result[2], true, "insertCompositionText", "\u3042",
"runRedundantChangeTest: after synthesizing composition change #1");
is(textarea.value, "\u3042", "runRedundantChangeTest: textarea has uncommitted string #1");
@ -6120,7 +6142,7 @@ function runRedundantChangeTest()
"runRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string #2");
is(result[2].type, "input",
"runRedundantChangeTest: input should be fired after synthesizing composition change #2");
checkInputEvent(result[2], true, "insertCompositionText",
checkInputEvent(result[2], true, "insertCompositionText", "\u3042\u3044",
"runRedundantChangeTest: after synthesizing composition change #2");
is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has uncommitted string #2");
@ -6151,7 +6173,7 @@ function runRedundantChangeTest()
"runRedundantChangeTest: compositionend should be fired after synthesizing composition commit-as-is");
is(result[2].type, "input",
"runRedundantChangeTest: input shouldn't be fired before compositionend at synthesizing commit-as-is");
checkInputEvent(result[2], false, "insertCompositionText",
checkInputEvent(result[2], false, "insertCompositionText", "\u3042\u3044",
"runRedundantChangeTest: at synthesizing commit-as-is");
is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has the commit string");
@ -6204,7 +6226,7 @@ function runNotRedundantChangeTest()
"runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges");
is(result[2].type, "input",
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges");
checkInputEvent(result[2], true, "insertCompositionText",
checkInputEvent(result[2], true, "insertCompositionText", "ABCDE",
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges");
is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #1");
@ -6225,7 +6247,7 @@ function runNotRedundantChangeTest()
"runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with null ranges after non-null ranges");
is(result[1].type, "input",
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with null ranges after non-null ranges");
checkInputEvent(result[1], true, "insertCompositionText",
checkInputEvent(result[1], true, "insertCompositionText", "ABCDE",
"runNotRedundantChangeTest: after synthesizing composition change with null ranges after non-null ranges");
is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #2");
@ -6248,7 +6270,7 @@ function runNotRedundantChangeTest()
"runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges after null ranges");
is(result[1].type, "input",
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges after null ranges");
checkInputEvent(result[1], true, "insertCompositionText",
checkInputEvent(result[1], true, "insertCompositionText", "ABCDE",
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges after null ranges");
is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #3");
@ -6271,7 +6293,7 @@ function runNotRedundantChangeTest()
"runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with empty data and null ranges after non-null ranges");
is(result[2].type, "input",
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
checkInputEvent(result[2], true, "insertCompositionText",
checkInputEvent(result[2], true, "insertCompositionText", "",
"runNotRedundantChangeTest: after synthesizing composition change with empty data and null ranges after non-null ranges");
is(textarea.value, "abcde", "runNotRedundantChangeTest: textarea doesn't have uncommitted string #1");
@ -6296,7 +6318,7 @@ function runNotRedundantChangeTest()
"runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges after empty data and null ranges");
is(result[2].type, "input",
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
checkInputEvent(result[2], true, "insertCompositionText",
checkInputEvent(result[2], true, "insertCompositionText", "ABCDE",
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges after empty data and null ranges");
is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #4");
@ -6313,7 +6335,7 @@ function runNotRedundantChangeTest()
"runNotRedundantChangeTest: compositionend should be fired after synthesizing composition commit with empty data after non-empty data");
is(result[3].type, "input",
"runNotRedundantChangeTest: input should be fired after compositionend after synthesizing composition change with empty data after non-empty data");
checkInputEvent(result[3], false, "insertCompositionText",
checkInputEvent(result[3], false, "insertCompositionText", "",
"runNotRedundantChangeTest: after synthesizing composition change with empty data after non-empty data");
is(textarea.value, "abcde", "runNotRedundantChangeTest: textarea doesn't have uncommitted string #2");