SSL window_composition_text_querycontent.xhtml
Sprache: unbekannt
|
|
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window title="Testing composition, text and query content events"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
<script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js" />
<panel id="panel" hidden="true" orient="vertical">
<vbox id="vbox">
<html:textarea id="textbox" cols="20" rows="4" style="font-size: 36px;"/>
</vbox>
</panel>
<body xmlns="http://www.w3.org/1999/xhtml">
<div id="display">
<div id="div" style="margin: 0; padding: 0; font-size: 36px;">Here is a text frame.</div>
<textarea style="margin: 0; font-family: -moz-fixed;" id="textarea" cols="20" rows="4"></textarea><br/>
<iframe id="iframe" width="300" height="150"
src="data:text/html,<textarea id='textarea' cols='20' rows='4'></textarea>"></iframe><br/>
<iframe id="iframe2" width="300" height="150"
src="data:text/html,<body onload='document.designMode=%22on%22'>body content</body>"></iframe><br/>
<iframe id="iframe3" width="300" height="150"
src="data:text/html,<body onload='document.designMode=%22on%22'>body content</body>"></iframe><br/>
<iframe id="iframe4" width="300" height="150"
src="data:text/html,<div contenteditable id='contenteditable'></div>"></iframe><br/>
<!--
NOTE: the width for the next two iframes is chosen to be small enough to make
the Show Password button (for type=password) be outside the viewport so that
it doesn't affect the rendering compared to the type=text control.
But still large enough to comfortably fit the input values we test.
-->
<iframe id="iframe5" style="width:10ch" height="50" src="data:text/html,<input id='input'>"></iframe>
<iframe id="iframe6" style="width:10ch" height="50" src="data:text/html,<input id='password' type='password'>"></iframe><br/>
<iframe id="iframe7" width="300" height="150"
src="data:text/html,<span contenteditable id='contenteditable'></span>"></iframe><br/>
<input id="input" type="text"/><br/>
<input id="password" type="password"/><br/>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
<script class="testbody" type="application/javascript">
<![CDATA[
function ok(aCondition, aMessage)
{
window.arguments[0].SimpleTest.ok(aCondition, aMessage);
}
function is(aLeft, aRight, aMessage)
{
window.arguments[0].SimpleTest.is(aLeft, aRight, aMessage);
}
function isnot(aLeft, aRight, aMessage)
{
window.arguments[0].SimpleTest.isnot(aLeft, aRight, aMessage);
}
function isfuzzy(aLeft, aRight, aEpsilon, aMessage) {
window.arguments[0].SimpleTest.isfuzzy(aLeft, aRight, aEpsilon, aMessage);
}
function todo(aCondition, aMessage)
{
window.arguments[0].SimpleTest.todo(aCondition, aMessage);
}
function todo_is(aLeft, aRight, aMessage)
{
window.arguments[0].SimpleTest.todo_is(aLeft, aRight, aMessage);
}
function todo_isnot(aLeft, aRight, aMessage)
{
window.arguments[0].SimpleTest.todo_isnot(aLeft, aRight, aMessage);
}
function isSimilarTo(aLeft, aRight, aAllowedDifference, aMessage)
{
if (Math.abs(aLeft - aRight) <= aAllowedDifference) {
ok(true, aMessage);
} else {
ok(false, aMessage + ", got=" + aLeft + ", expected=" + (aRight - aAllowedDifference) + "~" + (aRight + aAllowedDifference));
}
}
function isGreaterThan(aLeft, aRight, aMessage)
{
ok(aLeft > aRight, aMessage + ", got=" + aLeft + ", expected minimum value=" + aRight);
}
/**
* synthesizeSimpleCompositionChange synthesizes a composition which has only
* one clause and put caret end of it.
*
* @param aComposition string or object. If string, it's treated as
* composition string whose attribute is
* COMPOSITION_ATTR_RAW_CLAUSE.
* If object, it must have .string whose type is "string".
* Additionally, .attr can be specified if you'd like to
* use the other attribute instead of
* COMPOSITION_ATTR_RAW_CLAUSE.
*/
function synthesizeSimpleCompositionChange(aComposition, aWindow, aCallback) {
const comp = (() => {
if (typeof aComposition == "string") {
return { string: aComposition, attr: COMPOSITION_ATTR_RAW_CLAUSE };
}
return {
string: aComposition.string,
attr: aComposition.attr === undefined
? COMPOSITION_ATTR_RAW_CLAUSE
: aComposition.attr
};
})();
synthesizeCompositionChange(
{
composition: {
string: comp.string,
clauses: [
{ length: comp.string.length, attr: comp.attr },
],
},
caret: { start: comp.string.length, length: 0 },
},
aWindow,
aCallback
);
}
var div = document.getElementById("div");
var textarea = document.getElementById("textarea");
var panel = document.getElementById("panel");
var textbox = document.getElementById("textbox");
var iframe = document.getElementById("iframe");
var iframe2 = document.getElementById("iframe2");
var iframe3 = document.getElementById("iframe3");
var contenteditable;
var windowOfContenteditable;
var contenteditableBySpan;
var windowOfContenteditableBySpan;
var input = document.getElementById("input");
var password = document.getElementById("password");
var textareaInFrame;
const nsITextInputProcessorCallback = Ci.nsITextInputProcessorCallback;
const nsIInterfaceRequestor = Ci.nsIInterfaceRequestor;
const nsIWebNavigation = Ci.nsIWebNavigation;
const nsIDocShell = Ci.nsIDocShell;
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
const kExpectInputBeforeCompositionEnd = SpecialPowers.getBoolPref("dom.input_events.dispatch_before_compositionend");
function waitForTick() {
return new Promise(resolve => { SimpleTest.executeSoon(resolve); });
}
async function waitForEventLoops(aTimes)
{
for (let i = 1; i < aTimes; i++) {
await waitForTick();
}
await new Promise(resolve => { setTimeout(resolve, 20); });
}
function getEditor(aNode)
{
return aNode.editor;
}
function getHTMLEditorIMESupport(aWindow)
{
return aWindow.docShell.editor;
}
const kIsWin = (navigator.platform.indexOf("Win") == 0);
const kIsMac = (navigator.platform.indexOf("Mac") == 0);
const kLFLen = (kIsWin && false) ? 2 : 1;
const kLF = (kIsWin && false) ? "\r\n" : "\n";
function checkQueryContentResult(aResult, aMessage)
{
ok(aResult, aMessage + ": the result is null");
if (!aResult) {
return false;
}
ok(aResult.succeeded, aMessage + ": the query content failed");
return aResult.succeeded;
}
function checkContent(aExpectedText, aMessage, aID)
{
if (!aID) {
aID = "";
}
let textContent = synthesizeQueryTextContent(0, 100);
if (!checkQueryContentResult(textContent, aMessage +
": synthesizeQueryTextContent " + aID)) {
return false;
}
is(textContent.text, aExpectedText,
aMessage + ": composition string is wrong " + aID);
return textContent.text == aExpectedText;
}
function checkContentRelativeToSelection(aRelativeOffset, aLength, aExpectedOffset, aExpectedText, aMessage, aID)
{
if (!aID) {
aID = "";
}
aMessage += " (aRelativeOffset=" + aRelativeOffset + "): "
let textContent = synthesizeQueryTextContent(aRelativeOffset, aLength, true);
if (!checkQueryContentResult(textContent, aMessage +
"synthesizeQueryTextContent " + aID)) {
return false;
}
is(textContent.offset, aExpectedOffset,
aMessage + "offset is wrong " + aID);
is(textContent.text, aExpectedText,
aMessage + "text is wrong " + aID);
return textContent.offset == aExpectedOffset &&
textContent.text == aExpectedText;
}
function checkSelection(aExpectedOffset, aExpectedText, aMessage, aID)
{
if (!aID) {
aID = "";
}
let selectedText = synthesizeQuerySelectedText();
if (!checkQueryContentResult(selectedText, aMessage +
": synthesizeQuerySelectedText " + aID)) {
return false;
}
if (aExpectedOffset === null) {
is(
selectedText.notFound,
true,
`${aMessage}: selection should not be found ${aID}`
);
return selectedText.notFound;
}
is(
selectedText.notFound,
false,
`${aMessage}: selection should be found ${aID}`
);
if (selectedText.notFound) {
return false;
}
is(
selectedText.offset,
aExpectedOffset,
`${aMessage}: selection offset should be ${aExpectedOffset} ${aID}`
);
is(
selectedText.text,
aExpectedText,
`${aMessage}: selected text should be "${aExpectedText}" ${aID}`
);
return selectedText.offset == aExpectedOffset &&
selectedText.text == aExpectedText;
}
function checkIMESelection(
aSelectionType,
aExpectedFound,
aExpectedOffset,
aExpectedText,
aMessage,
aID,
aToDo = {}
) {
if (!aID) {
aID = "";
}
aMessage += " (" + aSelectionType + ")";
let {
notFound = is,
offset = is,
text = is,
} = aToDo;
let selectionType = 0;
switch (aSelectionType) {
case "RawClause":
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_RAWINPUT;
break;
case "SelectedRawClause":
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDRAWTEXT;
break;
case "ConvertedClause":
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_CONVERTEDTEXT;
break;
case "SelectedClause":
selectionType = QUERY_CONTENT_FLAG_SELECTION_IME_SELECTEDCONVERTEDTEXT;
break;
default:
ok(false, aMessage + ": invalid selection type, " + aSelectionType);
}
isnot(selectionType, 0, aMessage + ": wrong value");
let selectedText = synthesizeQuerySelectedText(selectionType);
if (!checkQueryContentResult(selectedText, aMessage +
": synthesizeQuerySelectedText " + aID)) {
return false;
}
notFound(
selectedText.notFound,
!aExpectedFound,
`${aMessage}: selection should ${
aExpectedFound ? "" : "not"
} be found ${aID}`);
if (selectedText.notFound) {
return selectedText.notFound == !aExpectedFound;
}
offset(
selectedText.offset,
aExpectedOffset,
`${aMessage}: selection offset is wrong ${aID}`
);
text(
selectedText.text,
aExpectedText,
`${aMessage}: selected text is wrong ${aID}`
);
return selectedText.offset == aExpectedOffset &&
selectedText.text == aExpectedText;
}
function checkRect(aRect, aExpectedRect, aMessage)
{
is(aRect.left, aExpectedRect.left, aMessage + ": left is wrong");
is(aRect.top, aExpectedRect.top, aMessage + " top is wrong");
is(aRect.width, aExpectedRect.width, aMessage + ": width is wrong");
is(aRect.height, aExpectedRect.height, aMessage + ": height is wrong");
return aRect.left == aExpectedRect.left &&
aRect.top == aExpectedRect.top &&
aRect.width == aExpectedRect.width &&
aRect.height == aExpectedRect.height;
}
function checkRectFuzzy(aRect, aExpectedRect, aEpsilon, aMessage) {
isfuzzy(aRect.left, aExpectedRect.left, aEpsilon.left, aMessage + ": left is wrong");
isfuzzy(aRect.top, aExpectedRect.top, aEpsilon.top, aMessage + " top is wrong");
isfuzzy(aRect.width, aExpectedRect.width, aEpsilon.width, aMessage + ": width is wrong");
isfuzzy(aRect.height, aExpectedRect.height, aEpsilon.height, aMessage + ": height is wrong");
return (aRect.left >= aExpectedRect.left - aEpsilon.left &&
aRect.left <= aExpectedRect.left + aEpsilon.left) &&
(aRect.top >= aExpectedRect.top - aEpsilon.top &&
aRect.top <= aExpectedRect.top + aEpsilon.top) &&
(aRect.width >= aExpectedRect.width - aEpsilon.width &&
aRect.width <= aExpectedRect.width + aEpsilon.width) &&
(aRect.height >= aExpectedRect.height - aEpsilon.height &&
aRect.height <= aExpectedRect.height + aEpsilon.height);
}
function getRectArray(aQueryTextRectArrayResult) {
let rects = [];
for (let i = 0; ; i++) {
let rect = { left: {}, top: {}, width: {}, height: {} };
try {
aQueryTextRectArrayResult.getCharacterRect(i, rect.left, rect.top, rect.width, rect.height);
} catch (e) {
break;
}
rects.push({
left: rect.left.value,
top: rect.top.value,
width: rect.width.value,
height: rect.height.value,
});
}
return rects;
}
function checkRectArray(aQueryTextRectArrayResult, aExpectedTextRectArray, aMessage)
{
for (let i = 1; i < aExpectedTextRectArray.length; ++i) {
let rect = { left: {}, top: {}, width: {}, height: {} };
try {
aQueryTextRectArrayResult.getCharacterRect(i, rect.left, rect.top, rect.width, rect.height);
} catch (e) {
ok(false, aMessage + ": failed to retrieve " + i + "th rect (" + e + ")");
return false;
}
function toRect(aRect)
{
return { left: aRect.left.value, top: aRect.top.value, width: aRect.width.value, height: aRect.height.value };
}
if (!checkRect(toRect(rect), aExpectedTextRectArray[i], aMessage + " " + i + "th rect")) {
return false;
}
}
return true;
}
function checkRectContainsRect(aRect, aContainer, aMessage)
{
let container = { left: Math.ceil(aContainer.left),
top: Math.ceil(aContainer.top),
width: Math.floor(aContainer.width),
height: Math.floor(aContainer.height) };
let ret = container.left <= aRect.left &&
container.top <= aRect.top &&
container.left + container.width >= aRect.left + aRect.width &&
container.top + container.height >= aRect.top + aRect.height;
ret = ret && aMessage;
ok(ret, aMessage + " container={ left=" + container.left + ", top=" +
container.top + ", width=" + container.width + ", height=" +
container.height + " } rect={ left=" + aRect.left + ", top=" + aRect.top +
", width=" + aRect.width + ", height=" + aRect.height + " }");
return ret;
}
// eslint-disable-next-line complexity
function runUndoRedoTest()
{
textarea.value = "";
textarea.focus();
// input raw characters
synthesizeCompositionChange(
{ "composition":
{ "string": "\u306D",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "," },
});
synthesizeCompositionChange(
{ "composition":
{ "string": "\u306D\u3053",
"clauses":
[
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "b" },
});
// convert
synthesizeCompositionChange(
{ "composition":
{ "string": "\u732B",
"clauses":
[
{ "length": 1,
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: " " },
});
// commit
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
// input raw characters
synthesizeCompositionChange(
{ "composition":
{ "string": "\u307E",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "j" },
});
// cancel the composition
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Escape" } });
// input raw characters
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3080",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "]" },
});
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3080\u3059",
"clauses":
[
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "r" },
});
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3080\u3059\u3081",
"clauses":
[
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 },
"key": { key: "/" },
});
// convert
synthesizeCompositionChange(
{ "composition":
{ "string": "\u5A18",
"clauses":
[
{ "length": 1,
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: " " },
});
// commit
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
sendString(" meant");
synthesizeKey("KEY_Backspace");
synthesizeKey("s \"cat-girl\". She is a ");
// input raw characters
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3088",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "9" },
});
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3088\u3046",
"clauses":
[
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "4" },
});
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3088\u3046\u304b",
"clauses":
[
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 },
"key": { key: "t" },
});
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3088\u3046\u304b\u3044",
"clauses":
[
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 4, "length": 0 },
"key": { key: "e" },
});
// convert
synthesizeCompositionChange(
{ "composition":
{ "string": "\u5996\u602a",
"clauses":
[
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: " " },
});
// commit
synthesizeComposition({ type: "compositioncommitasis", key: { key: "Enter" } });
synthesizeKey("KEY_Backspace", {repeat: 12});
let i = 0;
if (!checkContent("\u732B\u5A18 means \"cat-girl\".",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(20, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a \u5996\u602A",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(32, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a ",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(30, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("\u732B\u5A18 mean",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(7, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("\u732B\u5A18 meant",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(8, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("\u732B\u5A18",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(2, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("\u732B",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
// XXX this is unexpected behavior, see bug 258291
if (!checkContent("\u732B",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(0, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true});
if (!checkContent("",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(0, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
// XXX this is unexpected behavior, see bug 258291
if (!checkContent("\u732B",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(1, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(2, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18 meant",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(8, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18 mean",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(7, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a ",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(30, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18 means \"cat-girl\". She is a \u5996\u602A",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(32, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18 means \"cat-girl\".",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(20, "", "runUndoRedoTest", "#" + i)) {
return;
}
synthesizeKey("Z", {accelKey: true, shiftKey: true});
if (!checkContent("\u732B\u5A18 means \"cat-girl\".",
"runUndoRedoTest", "#" + ++i) ||
!checkSelection(20, "", "runUndoRedoTest", "#" + i)) {
// eslint-disable-next-line no-useless-return
return;
}
}
function checkInputEvent(aEvent, aIsComposing, aInputType, aData, aTargetRanges, aDescription) {
if (aEvent.type !== "input" && aEvent.type !== "beforeinput") {
throw new Error(`${aDescription}: "${aEvent.type}" is not InputEvent`);
}
ok(InputEvent.isInstance(aEvent), `"${aEvent.type}" event should be dispatched with InputEvent interface: ${aDescription}`);
let cancelable = aEvent.type === "beforeinput" &&
aInputType !== "insertCompositionText" &&
aInputType !== "deleteCompositionText";
is(aEvent.cancelable, cancelable, `"${aEvent.type}" event should ${cancelable ? "be" : "be never"} cancelable: ${aDescription}`);
is(aEvent.bubbles, true, `"${aEvent.type}" event should always bubble: ${aDescription}`);
is(aEvent.isComposing, aIsComposing, `isComposing of "${aEvent.type}" event should be ${aIsComposing}: ${aDescription}`);
is(aEvent.inputType, aInputType, `inputType of "${aEvent.type}" event should be "${aInputType}": ${aDescription}`);
is(aEvent.data, aData, `data of "${aEvent.type}" event should be ${aData}: ${aDescription}`);
is(aEvent.dataTransfer, null, `dataTransfer of "${aEvent.type}" event should be null: ${aDescription}`);
let targetRanges = aEvent.getTargetRanges();
if (aTargetRanges.length === 0) {
is(targetRanges.length, 0,
`getTargetRange() of "${aEvent.type}" event should return empty array: ${aDescription}`);
} else {
is(targetRanges.length, aTargetRanges.length,
`getTargetRange() of "${aEvent.type}" event should return static range array: ${aDescription}`);
if (targetRanges.length == aTargetRanges.length) {
for (let i = 0; i < targetRanges.length; i++) {
is(targetRanges[i].startContainer, aTargetRanges[i].startContainer,
`startContainer of getTargetRanges()[${i}] of "${aEvent.type}" event does not match: ${aDescription}`);
is(targetRanges[i].startOffset, aTargetRanges[i].startOffset,
`startOffset of getTargetRanges()[${i}] of "${aEvent.type}" event does not match: ${aDescription}`);
is(targetRanges[i].endContainer, aTargetRanges[i].endContainer,
`endContainer of getTargetRanges()[${i}] of "${aEvent.type}" event does not match: ${aDescription}`);
is(targetRanges[i].endOffset, aTargetRanges[i].endOffset,
`endOffset of getTargetRanges()[${i}] of "${aEvent.type}" event does not match: ${aDescription}`);
}
}
}
}
function checkTextInputEvent(aEvent, aData, aDescription) {
if (aEvent.type !== "textInput") {
throw new Error(`${aDescription}: "${aEvent.type}" is not TextEvent`);
}
ok(TextEvent.isInstance(aEvent), `"${aEvent.type}" event should be dispatched with TextEvent interface: ${aDescription}`);
is(aEvent.cancelable, true, `"${aEvent.type}" event should be cancelable: ${aDescription}`);
is(aEvent.bubbles, true, `"${aEvent.type}" event should always bubble: ${aDescription}`);
is(aEvent.data, aData, `data of "${aEvent.type}" event should be ${aData}: ${aDescription}`);
}
function runCompositionCommitAsIsTest()
{
textarea.focus();
let result = [];
function clearResult()
{
result = [];
}
function handler(aEvent)
{
result.push(aEvent);
}
textarea.addEventListener("compositionupdate", handler, true);
textarea.addEventListener("compositionend", handler, true);
textarea.addEventListener("beforeinput", handler, true);
textarea.addEventListener("textInput", handler, true);
textarea.addEventListener("input", handler, true);
textarea.addEventListener("text", handler, true);
// compositioncommitasis with composing string.
(() => {
textarea.value = "";
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #1");
is(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitAsIsTest: no textInput event should be fired before commit #1");
clearResult();
synthesizeComposition({ type: "compositioncommitasis", key: { key: "Enter" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
`runCompositionCommitAsIsTest: ${kExpectInputBeforeCompositionEnd ? 6 : 5} events should be fired after dispatching compositioncommitasis #1`);
let index = -1;
is(result[++index].type, "text",
"runCompositionCommitAsIsTest: text should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1");
is(result[++index].type, "beforeinput",
"runCompositionCommitAsIsTest: beforeinput should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1");
checkInputEvent(result[index], true, "insertCompositionText", "\u3042", [],
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1");
is(result[++index].type, "textInput",
"runCompositionCommitAsIsText: textInput should be fired after dispatching compositioncommitasis because it's after the last beforeinput for the composition #1");
checkTextInputEvent(result[index], "\u3042",
"runCompositionCommitAsIsText: after dispatching compositioncommitasis #1");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #1");
checkInputEvent(result[index], true, "insertCompositionText", "\u3042", [],
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1");
}
is(result[++index].type, "compositionend",
"runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #1");
is(result[++index].type, "input",
"runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #1");
checkInputEvent(result[index], false, "insertCompositionText", "\u3042", [],
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1");
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #1");
})();
// compositioncommitasis with committed string.
textarea.value = "";
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #2");
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2");
isnot(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitAsIsTest: a textInput event should be fired before commit #2");
clearResult();
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter", type: "keyup" } });
is(result.length, 2,
"runCompositionCommitAsIsTest: 2 events should be fired after dispatching compositioncommitasis #2");
// XXX Do we need a "beforeinput" event here? Not sure.
is(result[0].type, "compositionend",
"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", "\u3042", [],
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #2");
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2");
// compositioncommitasis with committed string.
textarea.value = "";
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #3");
synthesizeCompositionChange(
{ "composition":
{ "string": "",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Escape", type: "keydown" },
});
is(textarea.value, "", "runCompositionCommitAsIsTest: textarea has non-empty composition string #3");
todo_isnot(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitAsIsTest: a textInput event should be fired immediately before commit #3");
clearResult();
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape", type: "keyup" } });
is(result.length, 2,
"runCompositionCommitAsIsTest: 2 events should be fired after dispatching compositioncommitasis #3");
// XXX Do we need a "beforeinput" event here? Not sure.
is(result[0].type, "compositionend",
"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", "", [],
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #3");
is(textarea.value, "", "runCompositionCommitAsIsTest: textarea doesn't have committed string #3");
textarea.removeEventListener("compositionupdate", handler, true);
textarea.removeEventListener("compositionend", handler, true);
textarea.removeEventListener("beforeinput", handler, true);
textarea.removeEventListener("textInput", handler, true);
textarea.removeEventListener("input", handler, true);
textarea.removeEventListener("text", handler, true);
}
function runCompositionCommitTest()
{
textarea.focus();
let result = [];
function clearResult()
{
result = [];
}
function handler(aEvent)
{
result.push(aEvent);
}
textarea.addEventListener("compositionupdate", handler, true);
textarea.addEventListener("compositionend", handler, true);
textarea.addEventListener("beforeinput", handler, true);
textarea.addEventListener("textInput", handler, true);
textarea.addEventListener("input", handler, true);
textarea.addEventListener("text", handler, true);
// compositioncommit with different composing string.
(() => {
textarea.value = "";
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #1");
is(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitTest: no textInput event should be fired before commit #1");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "a", type: "keyup" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 7 : 6} events should be fired after dispatching compositioncommit #1`);
let index = -1;
is(result[++index].type, "compositionupdate",
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit because it's dispatched when there is composing string #1");
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #1");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit because it's dispatched when there is composing string #1");
checkInputEvent(result[index], true, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #1");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired after dispatching compositioncommit because the preceding beforeinput is last one for the composition #1");
checkTextInputEvent(result[index], "\u3043",
"runCompositionCommitTest: after dispatching compositioncommit #1");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #1");
checkInputEvent(result[index], true, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #1");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #1");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #1");
checkInputEvent(result[index], false, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #1");
is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #1");
})();
// compositioncommit with different committed string when there is already committed string
(() => {
textarea.value = "";
clearResult();
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #2");
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have committed string #2");
is(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitTest: no textInput event should be fired before commit #2");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 7 : 6} events should be fired after dispatching compositioncommit #2`);
let index = -1;
is(result[++index].type, "compositionupdate",
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #2");
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #2");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #2");
checkInputEvent(result[index], true, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #2");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #2");
checkTextInputEvent(result[index], "\u3043",
"runCompositionCommitTest: after dispatching compositioncommit #2");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #2");
checkInputEvent(result[index], true, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #2");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #2");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #2");
checkInputEvent(result[index], false, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #2");
is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #2");
})();
// compositioncommit with empty composition string.
(() => {
textarea.value = "";
clearResult();
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #3");
synthesizeCompositionChange(
{ "composition":
{ "string": "",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "", "runCompositionCommitTest: textarea has non-empty composition string #3");
is(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitTest: no textInput event should be fired before commit #3");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 7 : 6} events should be fired after dispatching compositioncommit #3`);
let index = -1;
is(result[++index].type, "compositionupdate",
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #3");
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #3");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #3");
checkInputEvent(result[index], true, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #3");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #3");
checkTextInputEvent(result[index], "\u3043",
"runCompositionCommitTest: after dispatching compositioncommit #3");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #3");
checkInputEvent(result[index], true, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #3");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #3");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #3");
checkInputEvent(result[index], false, "insertCompositionText", "\u3043", [],
"runCompositionCommitTest: after dispatching compositioncommit #3");
is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #3");
})();
// inserting empty string with simple composition.
(() => {
textarea.value = "abc";
textarea.setSelectionRange(3, 3);
synthesizeComposition({ type: "compositionstart" });
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "" });
is(result.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 6 : 5} events should be fired when inserting empty string with composition`);
let index = -1;
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired when inserting empty string with composition");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired when inserting empty string with composition");
checkInputEvent(result[index], true, "insertCompositionText", "", [],
"runCompositionCommitTest: when inserting empty string with composition");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired when inserting empty string with composition");
checkTextInputEvent(result[index], "",
"runCompositionCommitTest: when inserting empty string with composition");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired when inserting empty string with composition");
checkInputEvent(result[index], true, "insertCompositionText", "", [],
"runCompositionCommitTest: when inserting empty string with composition");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired when inserting empty string with composition");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired when inserting empty string with composition");
checkInputEvent(result[index], 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");
})();
// replacing selection with empty string with simple composition.
(() => {
textarea.value = "abc";
textarea.setSelectionRange(0, 3);
synthesizeComposition({ type: "compositionstart" });
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "" });
is(result.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 6 : 5} events should be fired when replacing with empty string with composition`);
let index = -1;
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired when replacing with empty string with composition");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired when replacing with empty string with composition");
checkInputEvent(result[index], true, "insertCompositionText", "", [],
"runCompositionCommitTest: when replacing with empty string with composition");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired when replacing with empty string with composition");
checkTextInputEvent(result[index], "",
"runCompositionCommitTest: when replacing with empty string with composition");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired when replacing with empty string with composition");
checkInputEvent(result[index], true, "insertCompositionText", "", [],
"runCompositionCommitTest: when replacing with empty string with composition");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired when replacing with empty string with composition");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired when replacing with empty string with composition");
checkInputEvent(result[index], 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");
})();
// replacing selection with same string with simple composition.
(() => {
textarea.value = "abc";
textarea.setSelectionRange(0, 3);
synthesizeComposition({ type: "compositionstart" });
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "abc" });
is(result.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
`runCompositionCommitTest: ${
kExpectInputBeforeCompositionEnd ? 7 : 6
} events should be fired when replacing selection with same string with composition`);
let index = -1;
is(result[++index].type, "compositionupdate",
"runCompositionCommitTest: compositionupdate should be fired when replacing selection with same string with composition");
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired when replacing selection with same string with composition");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired when replacing selection with same string with composition");
checkInputEvent(result[index], true, "insertCompositionText", "abc", [],
"runCompositionCommitTest: when replacing selection with same string with composition");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired when replacing selection with same string with composition");
checkTextInputEvent(result[index], "abc",
"runCompositionCommitTest: when replacing selection with same string with composition");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired when replacing selection with same string with composition");
checkInputEvent(result[index], true, "insertCompositionText", "abc", [],
"runCompositionCommitTest: when replacing selection with same string with composition");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired when replacing selection with same string with composition");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired when replacing selection with same string with composition");
checkInputEvent(result[index], 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");
})();
// compositioncommit with non-empty composition string.
(() => {
textarea.value = "";
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #4");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Enter" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 7 : 6} events should be fired after dispatching compositioncommit #4`);
let index = -1;
is(result[++index].type, "compositionupdate",
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #4");
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #4");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #4");
checkInputEvent(result[index], true, "insertCompositionText", "", [],
"runCompositionCommitTest: after dispatching compositioncommit #4");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #4");
checkTextInputEvent(result[index], "",
"runCompositionCommitTest: after dispatching compositioncommit #4");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #4");
checkInputEvent(result[index], true, "insertCompositionText", "", [],
"runCompositionCommitTest: after dispatching compositioncommit #4");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #4");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #4");
checkInputEvent(result[index], false, "insertCompositionText", "", [],
"runCompositionCommitTest: after dispatching compositioncommit #4");
is(textarea.value, "", "runCompositionCommitTest: textarea should be empty #4");
})();
// compositioncommit immediately without compositionstart
(() => {
textarea.value = "";
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "a" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 7 : 6,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 7 : 6} events should be fired after dispatching compositioncommit #5`);
let index = -1;
is(result[++index].type, "compositionupdate",
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #5");
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #5");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #5");
checkInputEvent(result[index], true, "insertCompositionText", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #5");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #5");
checkTextInputEvent(result[index], "\u3042",
"runCompositionCommitTest: after dispatching compositioncommit #5");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #5");
checkInputEvent(result[index], true, "insertCompositionText", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #5");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #5");
checkInputEvent(result[index], false, "insertCompositionText", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #5");
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should be empty #5");
})();
// compositioncommit with same composition string.
(() => {
textarea.value = "";
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #5");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "KEY_Enter" } });
is(result.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
`runCompositionCommitTest: ${kExpectInputBeforeCompositionEnd ? 6 : 5} events should be fired after dispatching compositioncommit #6`);
let index = -1;
is(result[++index].type, "text",
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #6");
is(result[++index].type, "beforeinput",
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #6");
checkInputEvent(result[index], true, "insertCompositionText", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #6");
is(result[++index].type, "textInput",
"runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #6");
checkTextInputEvent(result[index], "\u3042",
"runCompositionCommitTest: after dispatching compositioncommit #6");
if (kExpectInputBeforeCompositionEnd) {
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #6");
checkInputEvent(result[index], true, "insertCompositionText", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #6");
}
is(result[++index].type, "compositionend",
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #6");
is(result[++index].type, "input",
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #6");
checkInputEvent(result[index], false, "insertCompositionText", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #6");
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #6");
})();
// compositioncommit with same composition string when there is committed string
textarea.value = "";
clearResult();
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6");
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3042",
"clauses":
[
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6");
todo_isnot(result.findIndex(value => value.type == "textInput"), -1,
"runCompositionCommitTest: a textInput event should be fired before immediately commit #6");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "KEY_Enter", type: "keyup" } });
is(result.length, 2,
"runCompositionCommitTest: 2 events should be fired after dispatching compositioncommit #7");
// XXX Do we need a "beforeinput" event here? Not sure.
is(result[0].type, "compositionend",
"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", "\u3042", [],
"runCompositionCommitTest: after dispatching compositioncommit #7");
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #6");
textarea.removeEventListener("compositionupdate", handler, true);
textarea.removeEventListener("compositionend", handler, true);
textarea.removeEventListener("beforeinput", handler, true);
textarea.removeEventListener("textInput", handler, true);
textarea.removeEventListener("input", handler, true);
textarea.removeEventListener("text", handler, true);
}
// eslint-disable-next-line complexity
async function runCompositionTest()
{
textarea.value = "";
textarea.focus();
let caretRects = [];
let caretRect = synthesizeQueryCaretRect(0);
if (!checkQueryContentResult(caretRect,
"runCompositionTest: synthesizeQueryCaretRect #0")) {
return;
}
caretRects[0] = caretRect;
// input first character
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3089",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "o" },
});
if (!checkContent("\u3089", "runCompositionTest", "#1-1") ||
!checkSelection(1, "", "runCompositionTest", "#1-1")) {
return;
}
caretRect = synthesizeQueryCaretRect(1);
if (!checkQueryContentResult(caretRect,
"runCompositionTest: synthesizeQueryCaretRect #1-1")) {
return;
}
caretRects[1] = caretRect;
// input second character
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3089\u30FC",
"clauses":
[
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "\\", code: "IntlYen", keyCode: KeyboardEvent.DOM_VK_BACKSLASH },
});
if (!checkContent("\u3089\u30FC", "runCompositionTest", "#1-2") ||
!checkSelection(2, "", "runCompositionTest", "#1-2")) {
return;
}
caretRect = synthesizeQueryCaretRect(2);
if (!checkQueryContentResult(caretRect,
"runCompositionTest: synthesizeQueryCaretRect #1-2")) {
return;
}
caretRects[2] = caretRect;
isnot(caretRects[2].left, caretRects[1].left,
"runCompositionTest: caret isn't moved (#1-2)");
is(caretRects[2].top, caretRects[1].top,
"runCompositionTest: caret is moved to another line (#1-2)");
is(caretRects[2].width, caretRects[1].width,
"runCompositionTest: caret width is wrong (#1-2)");
is(caretRects[2].height, caretRects[1].height,
"runCompositionTest: caret width is wrong (#1-2)");
// input third character
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3089\u30FC\u3081",
"clauses":
[
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 },
"key": { key: "/" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3") ||
!checkSelection(3, "", "runCompositionTest", "#1-3")) {
return;
}
caretRect = synthesizeQueryCaretRect(3);
if (!checkQueryContentResult(caretRect,
"runCompositionTest: synthesizeQueryCaretRect #1-3")) {
return;
}
caretRects[3] = caretRect;
isnot(caretRects[3].left, caretRects[2].left,
"runCompositionTest: caret isn't moved (#1-3)");
is(caretRects[3].top, caretRects[2].top,
"runCompositionTest: caret is moved to another line (#1-3)");
is(caretRects[3].width, caretRects[2].width,
"runCompositionTest: caret width is wrong (#1-3)");
is(caretRects[3].height, caretRects[2].height,
"runCompositionTest: caret height is wrong (#1-3)");
// moves the caret left
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3089\u30FC\u3081",
"clauses":
[
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "KEY_ArrowLeft" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-1") ||
!checkSelection(2, "", "runCompositionTest", "#1-3-1")) {
return;
}
caretRect = synthesizeQueryCaretRect(2);
if (!checkQueryContentResult(caretRect,
"runCompositionTest: synthesizeQueryCaretRect #1-3-1")) {
return;
}
is(caretRect.left, caretRects[2].left,
"runCompositionTest: caret rects are different (#1-3-1, left)");
is(caretRect.top, caretRects[2].top,
"runCompositionTest: caret rects are different (#1-3-1, top)");
// by bug 335359, the caret width depends on the right side's character.
is(caretRect.width, caretRects[2].width + Math.round(window.devicePixelRatio),
"runCompositionTest: caret rects are different (#1-3-1, width)");
is(caretRect.height, caretRects[2].height,
"runCompositionTest: caret rects are different (#1-3-1, height)");
// moves the caret left
synthesizeCompositionChange(
{ "composition":
{ "string": "\u3089\u30FC\u3081",
"clauses":
[
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_ArrowLeft" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-2") ||
--> --------------------
--> maximum size reached
--> --------------------
[ Verzeichnis aufwärts0.176unsichere Verbindung
Übersetzung europäischer Sprachen durch Browser
]
|
2026-03-28
|