Chrome dispatches `compositionend` before `mousedown` when there is a composition and user clicks somewhere. However, we commit composition when `EventStateManager::PostHandleEvent` moves/clears focus after `eMouseDown` dispatching. Therefore, our `compositionend` event is fired after `mousedown`. The Chrome's behavior is simpler than us from mouse button event handlers point of view because it guarantees that there is no composition when handling mouse button events. We should follow this behavior for better compatibility and making the things simpler. Differential Revision: https://phabricator.services.mozilla.com/D236827
11424 lines
505 KiB
HTML
11424 lines
505 KiB
HTML
<?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") ||
|
|
!checkSelection(1, "", "runCompositionTest", "#1-3-2")) {
|
|
return;
|
|
}
|
|
|
|
|
|
caretRect = synthesizeQueryCaretRect(1);
|
|
if (!checkQueryContentResult(caretRect,
|
|
"runCompositionTest: synthesizeQueryCaretRect #1-3-2")) {
|
|
return;
|
|
}
|
|
|
|
is(caretRect.left, caretRects[1].left,
|
|
"runCompositionTest: caret rects are different (#1-3-2, left)");
|
|
is(caretRect.top, caretRects[1].top,
|
|
"runCompositionTest: caret rects are different (#1-3-2, top)");
|
|
// by bug 335359, the caret width depends on the right side's character.
|
|
is(caretRect.width, caretRects[1].width + Math.round(window.devicePixelRatio),
|
|
"runCompositionTest: caret rects are different (#1-3-2, width)");
|
|
is(caretRect.height, caretRects[1].height,
|
|
"runCompositionTest: caret rects are different (#1-3-2, height)");
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093",
|
|
"clauses":
|
|
[
|
|
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 },
|
|
"key": { key: "y" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-4") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#1-4")) {
|
|
return;
|
|
}
|
|
|
|
|
|
// backspace
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 },
|
|
"key": { key: "KEY_Backspace" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-5") ||
|
|
!checkSelection(3, "", "runCompositionTest", "#1-5")) {
|
|
return;
|
|
}
|
|
|
|
// re-input
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093",
|
|
"clauses":
|
|
[
|
|
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 },
|
|
"key": { key: "y" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-6") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#1-6")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055",
|
|
"clauses":
|
|
[
|
|
{ "length": 5, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 },
|
|
"key": { key: "x" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055", "runCompositionTest", "#1-7") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#1-7")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044",
|
|
"clauses":
|
|
[
|
|
{ "length": 6, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 6, "length": 0 },
|
|
"key": { key: "e" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044", "runCompositionTest", "#1-8") ||
|
|
!checkSelection(6, "", "runCompositionTest", "#1-8")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
|
|
"clauses":
|
|
[
|
|
{ "length": 7, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 7, "length": 0 },
|
|
"key": { key: "b" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053", "runCompositionTest", "#1-8") ||
|
|
!checkSelection(7, "", "runCompositionTest", "#1-8")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
|
|
"clauses":
|
|
[
|
|
{ "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 8, "length": 0 },
|
|
"key": { key: "4" },
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
|
|
"runCompositionTest", "#1-9") ||
|
|
!checkSelection(8, "", "runCompositionTest", "#1-9")) {
|
|
return;
|
|
}
|
|
|
|
// convert
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
|
"clauses":
|
|
[
|
|
{ "length": 4,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 2,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 },
|
|
"key": { key: " " },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
|
"runCompositionTest", "#1-10") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#1-10")) {
|
|
return;
|
|
}
|
|
|
|
// change the selected clause
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
|
"clauses":
|
|
[
|
|
{ "length": 4,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": 2,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 6, "length": 0 },
|
|
"key": { key: "KEY_ArrowLeft", shiftKey: true },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
|
"runCompositionTest", "#1-11") ||
|
|
!checkSelection(6, "", "runCompositionTest", "#1-11")) {
|
|
return;
|
|
}
|
|
|
|
// reset clauses
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
|
"clauses":
|
|
[
|
|
{ "length": 5,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 3,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 },
|
|
"key": { key: "KEY_ArrowRight" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
|
"runCompositionTest", "#1-12") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#1-12")) {
|
|
return;
|
|
}
|
|
|
|
|
|
let textRect1 = synthesizeQueryTextRect(0, 1);
|
|
let textRect2 = synthesizeQueryTextRect(1, 1);
|
|
if (!checkQueryContentResult(textRect1,
|
|
"runCompositionTest: synthesizeQueryTextRect #1-12-1") ||
|
|
!checkQueryContentResult(textRect2,
|
|
"runCompositionTest: synthesizeQueryTextRect #1-12-2")) {
|
|
return;
|
|
}
|
|
|
|
// commit the composition string
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
|
|
"runCompositionTest", "#1-13") ||
|
|
!checkSelection(8, "", "runCompositionTest", "#1-13")) {
|
|
return;
|
|
}
|
|
|
|
let textRect3 = synthesizeQueryTextRect(0, 1);
|
|
let textRect4 = synthesizeQueryTextRect(1, 1);
|
|
|
|
if (!checkQueryContentResult(textRect3,
|
|
"runCompositionTest: synthesizeQueryTextRect #1-13-1") ||
|
|
!checkQueryContentResult(textRect4,
|
|
"runCompositionTest: synthesizeQueryTextRect #1-13-2")) {
|
|
return;
|
|
}
|
|
|
|
checkRect(textRect3, textRect1, "runCompositionTest: textRect #1-13-1");
|
|
checkRect(textRect4, textRect2, "runCompositionTest: textRect #1-13-2");
|
|
|
|
// restart composition and input characters
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3057",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "d" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3057",
|
|
"runCompositionTest", "#2-1") ||
|
|
!checkSelection(8 + 1, "", "runCompositionTest", "#2-1")) {
|
|
return;
|
|
}
|
|
|
|
let textRect3QueriedWithRelativeOffset = synthesizeQueryTextRect(-8, 1, true);
|
|
let textRect4QueriedWithRelativeOffset = synthesizeQueryTextRect(-8 + 1, 1, true);
|
|
checkRect(textRect3QueriedWithRelativeOffset, textRect3, "runCompositionTest: textRect #2-1-2");
|
|
checkRect(textRect4QueriedWithRelativeOffset, textRect4, "runCompositionTest: textRect #2-1-3");
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3058",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "r" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058",
|
|
"runCompositionTest", "#2-2") ||
|
|
!checkSelection(8 + 1, "", "runCompositionTest", "#2-2")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3058\u3087",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 },
|
|
"key": { key: ")", code: "Digit9", keyCode: KeyboardEvent.DOM_VK_9, shiftKey: true },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087",
|
|
"runCompositionTest", "#2-3") ||
|
|
!checkSelection(8 + 2, "", "runCompositionTest", "#2-3")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3058\u3087\u3046",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 },
|
|
"key": { key: "4" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
|
|
"runCompositionTest", "#2-4") ||
|
|
!checkSelection(8 + 3, "", "runCompositionTest", "#2-4")) {
|
|
return;
|
|
}
|
|
|
|
// commit the composition string
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
|
|
"runCompositionTest", "#2-4") ||
|
|
!checkSelection(8 + 3, "", "runCompositionTest", "#2-4")) {
|
|
return;
|
|
}
|
|
|
|
// set selection
|
|
const selectionSetTest = await synthesizeSelectionSet(4, 7, false);
|
|
ok(selectionSetTest, "runCompositionTest: selectionSetTest failed");
|
|
|
|
if (!checkSelection(4, "\u3055\u884C\u3053\u3046\u3058\u3087\u3046", "runCompositionTest", "#3-1")) {
|
|
return;
|
|
}
|
|
|
|
// start composition with selection
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u304A",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "6" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u304A",
|
|
"runCompositionTest", "#3-2") ||
|
|
!checkSelection(4 + 1, "", "runCompositionTest", "#3-2")) {
|
|
return;
|
|
}
|
|
|
|
// remove the composition string
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
"caret": { "start": 0, "length": 0 },
|
|
"key": { key: "KEY_Backspace" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#3-3") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#3-3")) {
|
|
return;
|
|
}
|
|
|
|
// re-input the composition string
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3046",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "4" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3046",
|
|
"runCompositionTest", "#3-4") ||
|
|
!checkSelection(4 + 1, "", "runCompositionTest", "#3-4")) {
|
|
return;
|
|
}
|
|
|
|
// cancel the composition
|
|
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Escape" } });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#3-5") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#3-5")) {
|
|
return;
|
|
}
|
|
|
|
// bug 271815, some Chinese IMEs for Linux make empty composition string
|
|
// and compty clause information when it lists up Chinese characters on
|
|
// its candidate window.
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
"caret": { "start": 0, "length": 0 },
|
|
"key": { key: "a" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#4-1") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#4-1")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
"caret": { "start": 0, "length": 0 },
|
|
"key": { key: "b" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#4-2") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#4-2")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data: "\u6700", key: { key: "KEY_Enter" } });
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
|
|
"runCompositionTest", "#4-3") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#4-3")) {
|
|
return;
|
|
}
|
|
|
|
// testing the canceling case
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
"caret": { "start": 0, "length": 0 },
|
|
"key": { key: "a" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
|
|
"runCompositionTest", "#4-5") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#4-5")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape" } });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
|
|
"runCompositionTest", "#4-6") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#4-6")) {
|
|
return;
|
|
}
|
|
|
|
// testing whether the empty composition string deletes selected string.
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
"caret": { "start": 0, "length": 0 },
|
|
"key": { key: "a" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#4-8") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#4-8")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data: "\u9AD8", key: { key: "KEY_Enter" } });
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u9AD8",
|
|
"runCompositionTest", "#4-9") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#4-9")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeKey("KEY_Backspace");
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#4-11") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#4-11")) {
|
|
return;
|
|
}
|
|
|
|
// bug 23558, ancient Japanese IMEs on Window may send empty text event
|
|
// twice at canceling composition.
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u6700",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "a" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
|
|
"runCompositionTest", "#5-1") ||
|
|
!checkSelection(4 + 1, "", "runCompositionTest", "#5-1")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
"caret": { "start": 0, "length": 0 },
|
|
"key": { key: "KEY_Backspace", type: "keydown" },
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#5-2") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#5-2")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Backspace", type: "keyup" } });
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#5-3") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#5-3")) {
|
|
return;
|
|
}
|
|
|
|
// Undo tests for the testcases for bug 23558 and bug 271815
|
|
synthesizeKey("z", { accelKey: true });
|
|
|
|
// XXX this is unexpected behavior, see bug 258291
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#6-1") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#6-1")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeKey("z", { accelKey: true });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u9AD8",
|
|
"runCompositionTest", "#6-2") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#6-2")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeKey("z", { accelKey: true });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
|
|
"runCompositionTest", "#6-3") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#6-3")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeKey("z", { accelKey: true });
|
|
|
|
// XXX this is unexpected behavior, see bug 258291
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
|
|
"runCompositionTest", "#6-4") ||
|
|
!checkSelection(5, "", "runCompositionTest", "#6-4")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeKey("z", { accelKey: true });
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
|
|
"runCompositionTest", "#6-5") ||
|
|
!checkSelection(4, "", "runCompositionTest", "#6-5")) {
|
|
// eslint-disable-next-line no-useless-return
|
|
return;
|
|
}
|
|
}
|
|
|
|
function runCompositionEventTest()
|
|
{
|
|
const kDescription = "runCompositionEventTest: ";
|
|
const kEvents = ["compositionstart", "compositionupdate", "compositionend",
|
|
"input"];
|
|
|
|
input.value = "";
|
|
input.focus();
|
|
|
|
let windowEventCounts = [], windowEventData = [], windowEventLocale = [];
|
|
let inputEventCounts = [], inputEventData = [], inputEventLocale = [];
|
|
let preventDefault = false;
|
|
let stopPropagation = false;
|
|
|
|
function initResults()
|
|
{
|
|
for (let i = 0; i < kEvents.length; i++) {
|
|
windowEventCounts[kEvents[i]] = 0;
|
|
windowEventData[kEvents[i]] = "";
|
|
windowEventLocale[kEvents[i]] = "";
|
|
inputEventCounts[kEvents[i]] = 0;
|
|
inputEventData[kEvents[i]] = "";
|
|
inputEventLocale[kEvents[i]] = "";
|
|
}
|
|
}
|
|
|
|
function compositionEventHandlerForWindow(aEvent)
|
|
{
|
|
windowEventCounts[aEvent.type]++;
|
|
windowEventData[aEvent.type] = aEvent.data;
|
|
windowEventLocale[aEvent.type] = aEvent.locale;
|
|
if (preventDefault) {
|
|
aEvent.preventDefault();
|
|
}
|
|
if (stopPropagation) {
|
|
aEvent.stopPropagation();
|
|
}
|
|
}
|
|
|
|
function formEventHandlerForWindow(aEvent)
|
|
{
|
|
ok(aEvent.isTrusted, "input events must be trusted events");
|
|
windowEventCounts[aEvent.type]++;
|
|
windowEventData[aEvent.type] = input.value;
|
|
}
|
|
|
|
function compositionEventHandlerForInput(aEvent)
|
|
{
|
|
inputEventCounts[aEvent.type]++;
|
|
inputEventData[aEvent.type] = aEvent.data;
|
|
inputEventLocale[aEvent.type] = aEvent.locale;
|
|
if (preventDefault) {
|
|
aEvent.preventDefault();
|
|
}
|
|
if (stopPropagation) {
|
|
aEvent.stopPropagation();
|
|
}
|
|
}
|
|
|
|
function formEventHandlerForInput(aEvent)
|
|
{
|
|
inputEventCounts[aEvent.type]++;
|
|
inputEventData[aEvent.type] = input.value;
|
|
}
|
|
|
|
window.addEventListener("compositionstart", compositionEventHandlerForWindow,
|
|
true, true);
|
|
window.addEventListener("compositionend", compositionEventHandlerForWindow,
|
|
true, true);
|
|
window.addEventListener("compositionupdate", compositionEventHandlerForWindow,
|
|
true, true);
|
|
window.addEventListener("input", formEventHandlerForWindow,
|
|
true, true);
|
|
|
|
input.addEventListener("compositionstart", compositionEventHandlerForInput,
|
|
true, true);
|
|
input.addEventListener("compositionend", compositionEventHandlerForInput,
|
|
true, true);
|
|
input.addEventListener("compositionupdate", compositionEventHandlerForInput,
|
|
true, true);
|
|
input.addEventListener("input", formEventHandlerForInput,
|
|
true, true);
|
|
|
|
// test for normal case
|
|
initResults();
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "o" },
|
|
});
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by window #1");
|
|
is(windowEventData.compositionstart, "",
|
|
kDescription + "data of compositionstart isn't empty (window) #1");
|
|
is(windowEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty (window) #1");
|
|
is(inputEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by input #1");
|
|
is(inputEventData.compositionstart, "",
|
|
kDescription + "data of compositionstart isn't empty (input) #1");
|
|
is(inputEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty (input) #1");
|
|
|
|
is(windowEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by window #1");
|
|
is(windowEventData.compositionupdate, "\u3089",
|
|
kDescription + "data of compositionupdate doesn't match (window) #1");
|
|
is(windowEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (window) #1");
|
|
is(inputEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by input #1");
|
|
is(inputEventData.compositionupdate, "\u3089",
|
|
kDescription + "data of compositionupdate doesn't match (input) #1");
|
|
is(inputEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (input) #1");
|
|
|
|
is(windowEventCounts.compositionend, 0,
|
|
kDescription + "compositionend has been handled by window #1");
|
|
is(inputEventCounts.compositionend, 0,
|
|
kDescription + "compositionend has been handled by input #1");
|
|
|
|
is(windowEventCounts.input, 1,
|
|
kDescription + "input hasn't been handled by window #1");
|
|
is(windowEventData.input, "\u3089",
|
|
kDescription + "value of input element wasn't modified (window) #1");
|
|
is(inputEventCounts.input, 1,
|
|
kDescription + "input hasn't been handled by input #1");
|
|
is(inputEventData.input, "\u3089",
|
|
kDescription + "value of input element wasn't modified (input) #1");
|
|
|
|
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 },
|
|
});
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart has been handled more than once by window #2");
|
|
is(inputEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart has been handled more than once by input #2");
|
|
|
|
is(windowEventCounts.compositionupdate, 2,
|
|
kDescription + "compositionupdate hasn't been handled by window #2");
|
|
is(windowEventData.compositionupdate, "\u3089\u30FC",
|
|
kDescription + "data of compositionupdate doesn't match (window) #2");
|
|
is(windowEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (window) #2");
|
|
is(inputEventCounts.compositionupdate, 2,
|
|
kDescription + "compositionupdate hasn't been handled by input #2");
|
|
is(inputEventData.compositionupdate, "\u3089\u30FC",
|
|
kDescription + "data of compositionupdate doesn't match (input) #2");
|
|
is(inputEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (input) #2");
|
|
|
|
is(windowEventCounts.compositionend, 0,
|
|
kDescription + "compositionend has been handled during composition by window #2");
|
|
is(inputEventCounts.compositionend, 0,
|
|
kDescription + "compositionend has been handled during composition by input #2");
|
|
|
|
is(windowEventCounts.input, 2,
|
|
kDescription + "input hasn't been handled by window #2");
|
|
is(windowEventData.input, "\u3089\u30FC",
|
|
kDescription + "value of input element wasn't modified (window) #2");
|
|
is(inputEventCounts.input, 2,
|
|
kDescription + "input hasn't been handled by input #2");
|
|
is(inputEventData.input, "\u3089\u30FC",
|
|
kDescription + "value of input element wasn't modified (input) #2");
|
|
|
|
// text event shouldn't cause composition update, e.g., at committing.
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart has been handled more than once by window #3");
|
|
is(inputEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart has been handled more than once by input #3");
|
|
|
|
is(windowEventCounts.compositionupdate, 2,
|
|
kDescription + "compositionupdate has been fired unexpectedly on window #3");
|
|
is(inputEventCounts.compositionupdate, 2,
|
|
kDescription + "compositionupdate has been fired unexpectedly on input #3");
|
|
|
|
is(windowEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by window #3");
|
|
is(windowEventData.compositionend, "\u3089\u30FC",
|
|
kDescription + "data of compositionend doesn't match (window) #3");
|
|
is(windowEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty (window) #3");
|
|
is(inputEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by input #3");
|
|
is(inputEventData.compositionend, "\u3089\u30FC",
|
|
kDescription + "data of compositionend doesn't match (input) #3");
|
|
is(inputEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty (input) #3");
|
|
|
|
is(windowEventCounts.input, kExpectInputBeforeCompositionEnd ? 4 : 3,
|
|
kDescription + "input hasn't been handled by window #3");
|
|
is(windowEventData.input, "\u3089\u30FC",
|
|
kDescription + "value of input element wasn't modified (window) #3");
|
|
is(inputEventCounts.input, kExpectInputBeforeCompositionEnd ? 4 : 3,
|
|
kDescription + "input hasn't been handled by input #3");
|
|
is(inputEventData.input, "\u3089\u30FC",
|
|
kDescription + "value of input element wasn't modified (input) #3");
|
|
|
|
// select the second character, then, data of composition start should be
|
|
// the selected character.
|
|
initResults();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "o" },
|
|
});
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by window #4");
|
|
is(windowEventData.compositionstart, "\u30FC",
|
|
kDescription + "data of compositionstart is empty (window) #4");
|
|
is(windowEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty (window) #4");
|
|
is(inputEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by input #4");
|
|
is(inputEventData.compositionstart, "\u30FC",
|
|
kDescription + "data of compositionstart is empty (input) #4");
|
|
is(inputEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty (input) #4");
|
|
|
|
is(windowEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by window #4");
|
|
is(windowEventData.compositionupdate, "\u3089",
|
|
kDescription + "data of compositionupdate doesn't match (window) #4");
|
|
is(windowEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (window) #4");
|
|
is(inputEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by input #4");
|
|
is(inputEventData.compositionupdate, "\u3089",
|
|
kDescription + "data of compositionupdate doesn't match (input) #4");
|
|
is(inputEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (input) #4");
|
|
|
|
is(windowEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by window #4");
|
|
is(windowEventData.compositionend, "\u3089",
|
|
kDescription + "data of compositionend doesn't match (window) #4");
|
|
is(windowEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty (window) #4");
|
|
is(inputEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by input #4");
|
|
is(inputEventData.compositionend, "\u3089",
|
|
kDescription + "data of compositionend doesn't match (input) #4");
|
|
is(inputEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty (input) #4");
|
|
|
|
is(windowEventCounts.input, kExpectInputBeforeCompositionEnd ? 3 : 2,
|
|
kDescription + "input hasn't been handled by window #4");
|
|
is(windowEventData.input, "\u3089\u3089",
|
|
kDescription + "value of input element wasn't modified (window) #4");
|
|
is(inputEventCounts.input, kExpectInputBeforeCompositionEnd ? 3 : 2,
|
|
kDescription + "input hasn't been handled by input #4");
|
|
is(inputEventData.input, "\u3089\u3089",
|
|
kDescription + "value of input element wasn't modified (input) #4");
|
|
|
|
// preventDefault() should effect nothing.
|
|
preventDefault = true;
|
|
|
|
initResults();
|
|
synthesizeKey("a", { accelKey: true }); // Select All
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306D",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "," },
|
|
});
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by window #5");
|
|
is(windowEventData.compositionstart, "\u3089\u3089",
|
|
kDescription + "data of compositionstart is empty (window) #5");
|
|
is(windowEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty (window) #5");
|
|
is(inputEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by input #5");
|
|
is(inputEventData.compositionstart, "\u3089\u3089",
|
|
kDescription + "data of compositionstart is empty (input) #5");
|
|
is(inputEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty (input) #5");
|
|
|
|
is(windowEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by window #5");
|
|
is(windowEventData.compositionupdate, "\u306D",
|
|
kDescription + "data of compositionupdate doesn't match (window) #5");
|
|
is(windowEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (window) #5");
|
|
is(inputEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by input #5");
|
|
is(inputEventData.compositionupdate, "\u306D",
|
|
kDescription + "data of compositionupdate doesn't match (input) #5");
|
|
is(inputEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty (input) #5");
|
|
|
|
is(windowEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by window #5");
|
|
is(windowEventData.compositionend, "\u306D",
|
|
kDescription + "data of compositionend doesn't match (window) #5");
|
|
is(windowEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty (window) #5");
|
|
is(inputEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by input #5");
|
|
is(inputEventData.compositionend, "\u306D",
|
|
kDescription + "data of compositionend doesn't match (input) #5");
|
|
is(inputEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty (input) #5");
|
|
|
|
is(windowEventCounts.input, kExpectInputBeforeCompositionEnd ? 3 : 2,
|
|
kDescription + "input hasn't been handled by window #5");
|
|
is(windowEventData.input, "\u306D",
|
|
kDescription + "value of input element wasn't modified (window) #5");
|
|
is(inputEventCounts.input, kExpectInputBeforeCompositionEnd ? 3 : 2,
|
|
kDescription + "input hasn't been handled by input #5");
|
|
is(inputEventData.input, "\u306D",
|
|
kDescription + "value of input element wasn't modified (input) #5");
|
|
|
|
preventDefault = false;
|
|
|
|
// stopPropagation() should effect nothing (except event count)
|
|
stopPropagation = true;
|
|
|
|
initResults();
|
|
synthesizeKey("a", { accelKey: true }); // Select All
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "\\", code: "IntlRo", keyCode: KeyboardEvent.DOM_VK_BACKSLASH },
|
|
});
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by window #6");
|
|
is(windowEventData.compositionstart, "\u306D",
|
|
kDescription + "data of compositionstart is empty #6");
|
|
is(windowEventLocale.compositionstart, "",
|
|
kDescription + "locale of compositionstart isn't empty #6");
|
|
is(inputEventCounts.compositionstart, 0,
|
|
kDescription + "compositionstart has been handled by input #6");
|
|
|
|
is(windowEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by window #6");
|
|
is(windowEventData.compositionupdate, "\u306E",
|
|
kDescription + "data of compositionupdate doesn't match #6");
|
|
is(windowEventLocale.compositionupdate, "",
|
|
kDescription + "locale of compositionupdate isn't empty #6");
|
|
is(inputEventCounts.compositionupdate, 0,
|
|
kDescription + "compositionupdate has been handled by input #6");
|
|
|
|
is(windowEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by window #6");
|
|
is(windowEventData.compositionend, "\u306E",
|
|
kDescription + "data of compositionend doesn't match #6");
|
|
is(windowEventLocale.compositionend, "",
|
|
kDescription + "locale of compositionend isn't empty #6");
|
|
is(inputEventCounts.compositionend, 0,
|
|
kDescription + "compositionend has been handled by input #6");
|
|
|
|
is(windowEventCounts.input, kExpectInputBeforeCompositionEnd ? 3 : 2,
|
|
kDescription + "input hasn't been handled by window #6");
|
|
is(windowEventData.input, "\u306E",
|
|
kDescription + "value of input element wasn't modified (window) #6");
|
|
is(inputEventCounts.input, kExpectInputBeforeCompositionEnd ? 3 : 2,
|
|
kDescription + "input hasn't been handled by input #6");
|
|
is(inputEventData.input, "\u306E",
|
|
kDescription + "value of input element wasn't modified (input) #6");
|
|
|
|
stopPropagation = false;
|
|
|
|
// create event and dispatch it.
|
|
initResults();
|
|
|
|
input.value = "value of input";
|
|
synthesizeKey("a", { accelKey: true }); // Select All
|
|
|
|
let compositionstart = document.createEvent("CompositionEvent");
|
|
compositionstart.initCompositionEvent("compositionstart",
|
|
true, true, document.defaultView,
|
|
"start data", "start locale");
|
|
is(compositionstart.type, "compositionstart",
|
|
kDescription + "type doesn't match #7");
|
|
is(compositionstart.data, "start data",
|
|
kDescription + "data doesn't match #7");
|
|
is(compositionstart.locale, "start locale",
|
|
kDescription + "locale doesn't match #7");
|
|
is(compositionstart.detail, 0,
|
|
kDescription + "detail isn't 0 #7");
|
|
|
|
input.dispatchEvent(compositionstart);
|
|
|
|
is(windowEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by window #7");
|
|
is(windowEventData.compositionstart, "start data",
|
|
kDescription + "data of compositionstart was changed (window) #7");
|
|
is(windowEventLocale.compositionstart, "start locale",
|
|
kDescription + "locale of compositionstart was changed (window) #7");
|
|
is(inputEventCounts.compositionstart, 1,
|
|
kDescription + "compositionstart hasn't been handled by input #7");
|
|
is(inputEventData.compositionstart, "start data",
|
|
kDescription + "data of compositionstart was changed (input) #7");
|
|
is(inputEventLocale.compositionstart, "start locale",
|
|
kDescription + "locale of compositionstart was changed (input) #7");
|
|
|
|
is(input.value, "value of input",
|
|
kDescription + "input value was changed #7");
|
|
|
|
let compositionupdate1 = document.createEvent("compositionevent");
|
|
compositionupdate1.initCompositionEvent("compositionupdate",
|
|
true, false, document.defaultView,
|
|
"composing string", "composing locale");
|
|
is(compositionupdate1.type, "compositionupdate",
|
|
kDescription + "type doesn't match #8");
|
|
is(compositionupdate1.data, "composing string",
|
|
kDescription + "data doesn't match #8");
|
|
is(compositionupdate1.locale, "composing locale",
|
|
kDescription + "locale doesn't match #8");
|
|
is(compositionupdate1.detail, 0,
|
|
kDescription + "detail isn't 0 #8");
|
|
|
|
input.dispatchEvent(compositionupdate1);
|
|
|
|
is(windowEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by window #8");
|
|
is(windowEventData.compositionupdate, "composing string",
|
|
kDescription + "data of compositionupdate was changed (window) #8");
|
|
is(windowEventLocale.compositionupdate, "composing locale",
|
|
kDescription + "locale of compositionupdate was changed (window) #8");
|
|
is(inputEventCounts.compositionupdate, 1,
|
|
kDescription + "compositionupdate hasn't been handled by input #8");
|
|
is(inputEventData.compositionupdate, "composing string",
|
|
kDescription + "data of compositionupdate was changed (input) #8");
|
|
is(inputEventLocale.compositionupdate, "composing locale",
|
|
kDescription + "locale of compositionupdate was changed (input) #8");
|
|
|
|
is(input.value, "value of input",
|
|
kDescription + "input value was changed #8");
|
|
|
|
let compositionupdate2 = document.createEvent("compositionEvent");
|
|
compositionupdate2.initCompositionEvent("compositionupdate",
|
|
true, false, document.defaultView,
|
|
"commit string", "commit locale");
|
|
is(compositionupdate2.type, "compositionupdate",
|
|
kDescription + "type doesn't match #9");
|
|
is(compositionupdate2.data, "commit string",
|
|
kDescription + "data doesn't match #9");
|
|
is(compositionupdate2.locale, "commit locale",
|
|
kDescription + "locale doesn't match #9");
|
|
is(compositionupdate2.detail, 0,
|
|
kDescription + "detail isn't 0 #9");
|
|
|
|
input.dispatchEvent(compositionupdate2);
|
|
|
|
is(windowEventCounts.compositionupdate, 2,
|
|
kDescription + "compositionupdate hasn't been handled by window #9");
|
|
is(windowEventData.compositionupdate, "commit string",
|
|
kDescription + "data of compositionupdate was changed (window) #9");
|
|
is(windowEventLocale.compositionupdate, "commit locale",
|
|
kDescription + "locale of compositionupdate was changed (window) #9");
|
|
is(inputEventCounts.compositionupdate, 2,
|
|
kDescription + "compositionupdate hasn't been handled by input #9");
|
|
is(inputEventData.compositionupdate, "commit string",
|
|
kDescription + "data of compositionupdate was changed (input) #9");
|
|
is(inputEventLocale.compositionupdate, "commit locale",
|
|
kDescription + "locale of compositionupdate was changed (input) #9");
|
|
|
|
is(input.value, "value of input",
|
|
kDescription + "input value was changed #9");
|
|
|
|
let compositionend = document.createEvent("Compositionevent");
|
|
compositionend.initCompositionEvent("compositionend",
|
|
true, false, document.defaultView,
|
|
"end data", "end locale");
|
|
is(compositionend.type, "compositionend",
|
|
kDescription + "type doesn't match #10");
|
|
is(compositionend.data, "end data",
|
|
kDescription + "data doesn't match #10");
|
|
is(compositionend.locale, "end locale",
|
|
kDescription + "locale doesn't match #10");
|
|
is(compositionend.detail, 0,
|
|
kDescription + "detail isn't 0 #10");
|
|
|
|
input.dispatchEvent(compositionend);
|
|
|
|
is(windowEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by window #10");
|
|
is(windowEventData.compositionend, "end data",
|
|
kDescription + "data of compositionend was changed (window) #10");
|
|
is(windowEventLocale.compositionend, "end locale",
|
|
kDescription + "locale of compositionend was changed (window) #10");
|
|
is(inputEventCounts.compositionend, 1,
|
|
kDescription + "compositionend hasn't been handled by input #10");
|
|
is(inputEventData.compositionend, "end data",
|
|
kDescription + "data of compositionend was changed (input) #10");
|
|
is(inputEventLocale.compositionend, "end locale",
|
|
kDescription + "locale of compositionend was changed (input) #10");
|
|
|
|
is(input.value, "value of input",
|
|
kDescription + "input value was changed #10");
|
|
|
|
window.removeEventListener("compositionstart",
|
|
compositionEventHandlerForWindow, true);
|
|
window.removeEventListener("compositionend",
|
|
compositionEventHandlerForWindow, true);
|
|
window.removeEventListener("compositionupdate",
|
|
compositionEventHandlerForWindow, true);
|
|
window.removeEventListener("input",
|
|
formEventHandlerForWindow, true);
|
|
|
|
input.removeEventListener("compositionstart",
|
|
compositionEventHandlerForInput, true);
|
|
input.removeEventListener("compositionend",
|
|
compositionEventHandlerForInput, true);
|
|
input.removeEventListener("compositionupdate",
|
|
compositionEventHandlerForInput, true);
|
|
input.removeEventListener("input",
|
|
formEventHandlerForInput, true);
|
|
}
|
|
|
|
function runCompositionTestWhoseTextNodeModified() {
|
|
const selection = windowOfContenteditable.getSelection();
|
|
|
|
(function testInsertTextBeforeComposition() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testInsertTextBeforeComposition:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>def</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "def".length);
|
|
// Insert composition to the end of a text node
|
|
synthesizeSimpleCompositionChange("g");
|
|
is(
|
|
textNode.data,
|
|
"defg",
|
|
`${description} Composition should be inserted to end of the text node`
|
|
);
|
|
|
|
// Insert a character before the composition string
|
|
textNode.insertData(0, "c");
|
|
is(
|
|
textNode.data,
|
|
"cdefg",
|
|
`${
|
|
description
|
|
} Composition should be shifted when a character is inserted before it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}cdef`.length,
|
|
"g",
|
|
`${
|
|
description
|
|
} IME selection should be shifted when a character is inserted before it`
|
|
);
|
|
|
|
// Update composition string (appending a character)
|
|
synthesizeSimpleCompositionChange("gh");
|
|
is(
|
|
textNode.data,
|
|
"cdefgh",
|
|
`${
|
|
description
|
|
} Composition should be updated correctly after inserted a character before it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}cdef`.length,
|
|
"gh",
|
|
`${
|
|
description
|
|
} IME selection should be extended correctly at updating composition after inserted a character before it`
|
|
);
|
|
|
|
// Insert another character before the composition
|
|
textNode.insertData(0, "b");
|
|
is(
|
|
textNode.data,
|
|
"bcdefgh",
|
|
`${
|
|
description
|
|
} Composition should be shifted when a character is inserted again before it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}bcdef`.length,
|
|
"gh",
|
|
`${
|
|
description
|
|
} IME selection should be shifted when a character is inserted again before it`
|
|
);
|
|
|
|
// Update the composition string again (appending another character)
|
|
synthesizeSimpleCompositionChange("ghi");
|
|
is(
|
|
textNode.data,
|
|
"bcdefghi",
|
|
`${
|
|
description
|
|
} Composition should be updated correctly after inserted 2 characters before it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}bcdef`.length,
|
|
"ghi",
|
|
`${
|
|
description
|
|
} IME selection should be extended correctly at updating composition after inserted 2 characters before it`
|
|
);
|
|
|
|
// Insert a new character before the composition string
|
|
textNode.insertData(0, "a");
|
|
is(
|
|
textNode.data,
|
|
"abcdefghi",
|
|
`${
|
|
description
|
|
} Composition should be shifted when a character is inserted again and again before it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}abcdef`.length,
|
|
"ghi",
|
|
`${
|
|
description
|
|
} IME selection should be shifted when a character is inserted again and again before it`
|
|
);
|
|
|
|
// Commit the composition string
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abcdefghi",
|
|
`${
|
|
description
|
|
} Composition should be committed as is`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abcdefghi".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
|
|
// Undo the commit
|
|
synthesizeKey("z", { accelKey: true });
|
|
is(
|
|
textNode.data,
|
|
"abcdef",
|
|
`${
|
|
description
|
|
} Composition should be undone correctly`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abcdef".length,
|
|
`${
|
|
description
|
|
} Selection should be collapsed at where the composition was after undoing`
|
|
);
|
|
|
|
// Redo the commit
|
|
synthesizeKey("z", { accelKey: true, shiftKey: true });
|
|
is(
|
|
textNode.data,
|
|
"abcdefghi",
|
|
`${
|
|
description
|
|
} Composition should be redone correctly`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abcdefghi".length,
|
|
`${
|
|
description
|
|
} focus offset of Selection should be at end of the commit string after redoing`
|
|
);
|
|
})();
|
|
|
|
(function testInsertTextImmediatelyBeforeComposition() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testInsertTextImmediatelyBeforeComposition:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>d</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, 0);
|
|
// Insert composition at start of the text node
|
|
synthesizeSimpleCompositionChange("b");
|
|
is(
|
|
textNode.data,
|
|
"bd",
|
|
`${description} Composition should be inserted to start of the text node`
|
|
);
|
|
|
|
// Insert a character before the composition string
|
|
textNode.insertData(0, "a");
|
|
is(
|
|
textNode.data,
|
|
"abd",
|
|
`${
|
|
description
|
|
} Composition should be shifted when a character is inserted immediately before it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"b",
|
|
`${
|
|
description
|
|
} IME selection should be shifted when a character is inserted immediately before it`,
|
|
"",
|
|
{ offset: todo_is, text: todo_is }
|
|
);
|
|
|
|
// Update the composition string after inserting character immediately before it
|
|
synthesizeSimpleCompositionChange("bc");
|
|
is(
|
|
textNode.data,
|
|
"abcd",
|
|
`${description} Composition should be updated after the inserted character`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"bc",
|
|
`${
|
|
description
|
|
} IME selection should be set at the composition string after the inserted character`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abcd",
|
|
`${
|
|
description
|
|
} Composition should be committed after the inserted character`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abc".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
(function testInsertTextImmediatelyAfterComposition() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testInsertTextImmediatelyAfterComposition:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>a</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "a".length);
|
|
// Insert composition at end of the text node
|
|
synthesizeSimpleCompositionChange("b");
|
|
is(
|
|
textNode.data,
|
|
"ab",
|
|
`${description} Composition should be inserted to start of the text node`
|
|
);
|
|
|
|
// Insert a character after the composition string
|
|
textNode.insertData("ab".length, "d");
|
|
is(
|
|
textNode.data,
|
|
"abd",
|
|
`${
|
|
description
|
|
} Composition should stay when a character is inserted immediately after it`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"b",
|
|
`${
|
|
description
|
|
} IME selection should stay when a character is inserted immediately after it`
|
|
);
|
|
|
|
// Update the composition string after inserting character immediately after it
|
|
synthesizeSimpleCompositionChange("bc");
|
|
is(
|
|
textNode.data,
|
|
"abcd",
|
|
`${description} Composition should be updated before the inserted character`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"bc",
|
|
`${
|
|
description
|
|
} IME selection should be set at the composition string before the inserted character`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abcd",
|
|
`${
|
|
description
|
|
} Composition should be committed before the inserted character`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abc".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
// Inserting/replacing text before the last character of composition string
|
|
// should be contained by the composition, i.e., updated by next composition
|
|
// update. This is Chrome compatible.
|
|
(function testInsertTextMiddleOfComposition() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testInsertTextMiddleOfComposition:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>a</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "a".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("bd");
|
|
is(
|
|
textNode.data,
|
|
"abd",
|
|
`${description} Composition should be inserted to end of the text node`
|
|
);
|
|
|
|
// Insert a character before the composition string
|
|
textNode.insertData("ab".length, "c");
|
|
is(
|
|
textNode.data,
|
|
"abcd",
|
|
`${
|
|
description
|
|
} Inserted string should inserted into the middle of composition string`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"bcd",
|
|
`${
|
|
description
|
|
} IME selection should be extended when a character is inserted into middle of it`
|
|
);
|
|
|
|
// Update the composition string after inserting character into it
|
|
synthesizeSimpleCompositionChange("BD");
|
|
is(
|
|
textNode.data,
|
|
"aBD",
|
|
`${
|
|
description
|
|
} Composition should be replace the range containing the inserted character`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"BD",
|
|
`${
|
|
description
|
|
} IME selection should be set at the updated composition string`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"aBD",
|
|
`${
|
|
description
|
|
} Composition should be committed without the inserted character`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"aBD".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
(function testReplaceFirstCharOfCompositionString() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceFirstCharOfCompositionString:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted`
|
|
);
|
|
|
|
// Replace the composition string
|
|
textNode.replaceData("ab".length, "c".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"abXYZdefg",
|
|
`${description} First character of the composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"XYZde",
|
|
`${description} IME selection should contain the replace string`
|
|
);
|
|
|
|
// Update the composition string after replaced
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"abCDEfg",
|
|
`${description} Composition should update the replace string too`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"CDE",
|
|
`${description} IME selection should update the replace string too`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abCDEfg",
|
|
`${description} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
// Although Chrome commits composition if all composition string is removed,
|
|
// let's keep composition for making TSF stable...
|
|
(function testReplaceAllCompositionString() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceAllCompositionString:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted to the text node`
|
|
);
|
|
|
|
// Replace the composition string
|
|
textNode.replaceData("ab".length, "cde".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"abXYZfg",
|
|
`${description} Composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"",
|
|
`${
|
|
description
|
|
} IME selection should be collapsed before the replace string`
|
|
);
|
|
|
|
// Update the composition string after replaced
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"abCDEXYZfg",
|
|
`${description} Composition should be inserted again`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"CDE",
|
|
`${description} IME selection should not contain the replace string`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abCDEXYZfg",
|
|
`${description} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
(function testReplaceCompositionStringAndSurroundedCharacters() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceCompositionStringAndSurroundedCharacters:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted to the text node`
|
|
);
|
|
|
|
// Replace the composition string
|
|
textNode.replaceData("a".length, "bcdef".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"aXYZg",
|
|
`${description} Composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"",
|
|
`${
|
|
description
|
|
} IME selection should be collapsed before the replace string`
|
|
);
|
|
|
|
// Update the composition string after replaced
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"aCDEXYZg",
|
|
`${description} Composition should be inserted again`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"CDE",
|
|
`${description} IME selection should not contain the replace string`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"aCDEXYZg",
|
|
`${description} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"aCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
// If start boundary characters are replaced, the replace string should be
|
|
// contained into the composition range. This is Chrome compatible.
|
|
(function testReplaceStartBoundaryOfCompositionString() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceStartBoundaryOfCompositionString:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted to the text node`
|
|
);
|
|
|
|
// Replace some text
|
|
textNode.replaceData("a".length, "bc".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"aXYZdefg",
|
|
`${
|
|
description
|
|
} Start of the composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"XYZde",
|
|
`${description} IME selection should contain the replace string`
|
|
);
|
|
|
|
// Update the replace string and remaining composition.
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"aCDEfg",
|
|
`${description} Composition should update the replace string too`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}a`.length,
|
|
"CDE",
|
|
`${description} IME selection should contain the replace string`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"aCDEfg",
|
|
`${
|
|
description
|
|
} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"aCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
// If start boundary characters are replaced, the replace string should NOT
|
|
// be contained in the composition range. This is Chrome compatible.
|
|
(function testReplaceEndBoundaryOfCompositionString() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceEndBoundaryOfCompositionString:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted to the text node`
|
|
);
|
|
|
|
// Replace the composition string
|
|
textNode.replaceData("abcd".length, "ef".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"abcdXYZg",
|
|
`${
|
|
description
|
|
} End half of the composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"cd",
|
|
`${
|
|
description
|
|
} IME selection should be shrunken to the non-replaced part`
|
|
);
|
|
|
|
// Update the composition string after replaced
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"abCDEXYZg",
|
|
`${description} Only the remaining composition string should be updated`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"CDE",
|
|
`${description} IME selection should NOT include the replace string`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abCDEXYZg",
|
|
`${description} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
// If the last character of composition is replaced, i.e., it should NOT be
|
|
// treated as a part of composition string. This is Chrome compatible.
|
|
(function testReplaceLastCharOfCompositionString() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceLastCharOfCompositionString:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted`
|
|
);
|
|
|
|
// Replace the composition string
|
|
textNode.replaceData("abcd".length, "e".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"abcdXYZfg",
|
|
`${description} Last character of the composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"cd",
|
|
`${description} IME selection should be shrunken`
|
|
);
|
|
|
|
// Update the composition string after replaced
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"abCDEXYZfg",
|
|
`${description} Composition should NOT update the replace string`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"CDE",
|
|
`${description} IME selection should not contain the replace string`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abCDEXYZfg",
|
|
`${description} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
|
|
(function testReplaceMiddleCharOfCompositionString() {
|
|
const description =
|
|
"runCompositionTestWhoseTextNodeModified: testReplaceMiddleCharOfCompositionString:";
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p>abfg</p>";
|
|
const textNode = contenteditable.firstChild.firstChild;
|
|
selection.collapse(textNode, "ab".length);
|
|
// Insert composition at middle of the text node
|
|
synthesizeSimpleCompositionChange("cde");
|
|
is(
|
|
textNode.data,
|
|
"abcdefg",
|
|
`${description} Composition should be inserted`
|
|
);
|
|
|
|
// Replace the composition string
|
|
textNode.replaceData("abc".length, "d".length, "XYZ");
|
|
is(
|
|
textNode.data,
|
|
"abcXYZefg",
|
|
`${
|
|
description
|
|
} Middle character of the composition should be replaced`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"cXYZe",
|
|
`${description} IME selection should be extended by the replace string`
|
|
);
|
|
|
|
// Update the composition string after replaced
|
|
synthesizeSimpleCompositionChange("CDE");
|
|
is(
|
|
textNode.data,
|
|
"abCDEfg",
|
|
`${description} Composition should update the replace string`
|
|
);
|
|
checkIMESelection(
|
|
"RawClause",
|
|
true,
|
|
`${kLF}ab`.length,
|
|
"CDE",
|
|
`${description} IME selection should be shrunken after update`
|
|
);
|
|
|
|
// Commit it
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(
|
|
textNode.data,
|
|
"abCDEfg",
|
|
`${description} Composition should be committed`
|
|
);
|
|
is(
|
|
selection.focusOffset,
|
|
"abCDE".length,
|
|
`${description} Selection should be collapsed at end of the commit string`
|
|
);
|
|
})();
|
|
}
|
|
|
|
// eslint-disable-next-line complexity
|
|
function runQueryTextRectInContentEditableTest()
|
|
{
|
|
contenteditable.focus();
|
|
|
|
contenteditable.innerHTML = "<p>abc</p><p>def</p>";
|
|
// \n 0 123 4 567
|
|
// \r\n 01 234 56 789
|
|
|
|
let description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
|
|
|
|
// "a"
|
|
let a = synthesizeQueryTextRect(kLFLen, 1);
|
|
if (!checkQueryContentResult(a, description + "rect for 'a'")) {
|
|
return;
|
|
}
|
|
|
|
// "b"
|
|
let b = synthesizeQueryTextRect(kLFLen + 1, 1);
|
|
if (!checkQueryContentResult(b, description + "rect for 'b'")) {
|
|
return;
|
|
}
|
|
|
|
is(b.top, a.top, description + "'a' and 'b' should be at same top");
|
|
isSimilarTo(b.left, a.left + a.width, 2, description + "left of 'b' should be at similar to right of 'a'");
|
|
is(b.height, a.height, description + "'a' and 'b' should be same height");
|
|
|
|
// "c"
|
|
let c = synthesizeQueryTextRect(kLFLen + 2, 1);
|
|
if (!checkQueryContentResult(c, description + "rect for 'c'")) {
|
|
return;
|
|
}
|
|
|
|
is(c.top, b.top, description + "'b' and 'c' should be at same top");
|
|
isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
|
|
is(c.height, b.height, description + "'b' and 'c' should be same height");
|
|
|
|
// "abc" as array
|
|
let abcAsArray = synthesizeQueryTextRectArray(kLFLen, 3);
|
|
if (!checkQueryContentResult(abcAsArray, description + "rect array for 'abc'") ||
|
|
!checkRectArray(abcAsArray, [a, b, c], description + "query text rect array result of 'abc' should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// 2nd <p> (can be computed with the rect of 'c')
|
|
let p2 = synthesizeQueryTextRect(kLFLen + 3, 1);
|
|
if (!checkQueryContentResult(p2, description + "rect for 2nd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(p2.top, c.top, description + "'c' and a line breaker caused by 2nd <p> should be at same top");
|
|
isSimilarTo(p2.left, c.left + c.width, 2, description + "left of a line breaker caused by 2nd <p> should be at similar to right of 'c'");
|
|
is(p2.height, c.height, description + "'c' and a line breaker caused by 2nd <p> should be same height");
|
|
|
|
// 2nd <p> as array
|
|
let p2AsArray = synthesizeQueryTextRectArray(kLFLen + 3, 1);
|
|
if (!checkQueryContentResult(p2AsArray, description + "2nd <p>'s line breaker as array") ||
|
|
!checkRectArray(p2AsArray, [p2], description + "query text rect array result of 2nd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let p2_2 = synthesizeQueryTextRect(kLFLen + 4, 1);
|
|
if (!checkQueryContentResult(p2_2, description + "rect for \\n of \\r\\n caused by 2nd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(p2_2.top, p2.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(p2_2.left, p2.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(p2_2.height, p2.height, description + "'\\r' and '\\n' should be same height");
|
|
is(p2_2.width, p2.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let p2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 4, 1);
|
|
if (!checkQueryContentResult(p2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <p>") ||
|
|
!checkRectArray(p2_2AsArray, [p2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// "d"
|
|
let d = synthesizeQueryTextRect(kLFLen * 2 + 3, 1);
|
|
if (!checkQueryContentResult(d, description + "rect for 'd'")) {
|
|
return;
|
|
}
|
|
|
|
isGreaterThan(d.top, a.top + a.height, description + "top of 'd' should be greater than bottom of 'a'");
|
|
is(d.left, a.left, description + "'a' and 'd' should be same at same left");
|
|
is(d.height, a.height, description + "'a' and 'd' should be same height");
|
|
|
|
// "e"
|
|
let e = synthesizeQueryTextRect(kLFLen * 2 + 4, 1);
|
|
if (!checkQueryContentResult(e, description + "rect for 'e'")) {
|
|
return;
|
|
}
|
|
|
|
is(e.top, d.top, description + "'d' and 'd' should be at same top");
|
|
isSimilarTo(e.left, d.left + d.width, 2, description + "left of 'e' should be at similar to right of 'd'");
|
|
is(e.height, d.height, description + "'d' and 'e' should be same height");
|
|
|
|
// "f"
|
|
let f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
|
|
if (!checkQueryContentResult(f, description + "rect for 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(f.top, e.top, description + "'e' and 'f' should be at same top");
|
|
isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
|
|
is(f.height, e.height, description + "'e' and 'f' should be same height");
|
|
|
|
// "def" as array
|
|
let defAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 3, 3);
|
|
if (!checkQueryContentResult(defAsArray, description + "rect array for 'def'") ||
|
|
!checkRectArray(defAsArray, [d, e, f], description + "query text rect array result of 'def' should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// next of "f" (can be computed with rect of 'f')
|
|
let next_f = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
|
|
isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
|
|
is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
|
|
|
|
// next of "f" as array
|
|
let next_fAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(next_fAsArray, description + "rect array for next of 'f'") ||
|
|
!checkRectArray(next_fAsArray, [next_f], description + "query text rect array result of next of 'f' should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// too big offset for the editor
|
|
let tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
|
|
return;
|
|
}
|
|
|
|
is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
|
|
is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
|
|
is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
|
|
is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
|
|
|
|
// too big offset for the editors as array
|
|
let tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
|
|
!checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
contenteditable.innerHTML = "<p>abc</p><p>def</p><p><br></p>";
|
|
// \n 0 123 4 567 8 9
|
|
// \r\n 01 234 56 789 01 23
|
|
|
|
description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
|
|
|
|
// "f"
|
|
f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
|
|
if (!checkQueryContentResult(f, description + "rect for 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(f.top, e.top, description + "'e' and 'f' should be at same top");
|
|
is(f.height, e.height, description + "'e' and 'f' should be same height");
|
|
isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
|
|
|
|
// 3rd <p> (can be computed with rect of 'f')
|
|
let p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
|
|
is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
|
|
isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
|
|
|
|
// 3rd <p> as array
|
|
let p3AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(p3AsArray, description + "3rd <p>'s line breaker as array") ||
|
|
!checkRectArray(p3AsArray, [p3], description + "query text rect array result of 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(p3_2, description + "rect for \\n of \\r\\n caused by 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
|
|
is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let p3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(p3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <p>") ||
|
|
!checkRectArray(p3_2AsArray, [p3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// <br> in 3rd <p>
|
|
let br = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
|
|
if (!checkQueryContentResult(br, description + "rect for <br> in 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
isGreaterThan(br.top, d.top + d.height, description + "a line breaker caused by <br> in 3rd <p> should be greater than bottom of 'd'");
|
|
isSimilarTo(br.height, d.height, 2, description + "'d' and a line breaker caused by <br> in 3rd <p> should be similar height");
|
|
is(br.left, d.left, description + "left of a line breaker caused by <br> in 3rd <p> should be same left of 'd'");
|
|
|
|
// <br> in 3rd <p> as array
|
|
let brAsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
|
|
if (!checkQueryContentResult(brAsArray, description + "<br> in 3rd <p> as array") ||
|
|
!checkRectArray(brAsArray, [br], description + "query text rect array result of <br> in 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let br_2 = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
|
|
if (!checkQueryContentResult(br_2, description + "rect for \\n of \\r\\n caused by <br> in 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
|
|
is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let br_2AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 7, 1);
|
|
if (!checkQueryContentResult(br_2AsArray, description + "rect array for \\n of \\r\\n caused by <br> in 3rd <p>") ||
|
|
!checkRectArray(br_2AsArray, [br_2], description + "query text rect array result of \\n of \\r\\n caused by <br> in 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// next of <br> in 3rd <p>
|
|
let next_br = synthesizeQueryTextRect(kLFLen * 4 + 6, 1);
|
|
if (!checkQueryContentResult(next_br, description + "rect for next of <br> in 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(next_br.top, br.top, description + "next of <br> and <br> should be at same top");
|
|
is(next_br.left, br.left, description + "next of <br> and <br> should be at same left");
|
|
is(next_br.height, br.height, description + "next of <br> and <br> should be same height");
|
|
is(next_br.width, br.width, description + "next of <br> and <br> should be same width");
|
|
|
|
// next of <br> in 3rd <p> as array
|
|
let next_brAsArray = synthesizeQueryTextRectArray(kLFLen * 4 + 6, 1);
|
|
if (!checkQueryContentResult(next_brAsArray, description + "rect array for next of <br> in 3rd <p>") ||
|
|
!checkRectArray(next_brAsArray, [next_br], description + "query text rect array result of next of <br> in 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// too big offset for the editor
|
|
tooBigOffset = synthesizeQueryTextRect(kLFLen * 4 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
|
|
return;
|
|
}
|
|
|
|
is(tooBigOffset.top, next_br.top, description + "too big offset and next of 3rd <p> should be at same top");
|
|
is(tooBigOffset.left, next_br.left, description + "too big offset and next of 3rd <p> should be at same left");
|
|
is(tooBigOffset.height, next_br.height, description + "too big offset and next of 3rd <p> should be same height");
|
|
is(tooBigOffset.width, next_br.width, description + "too big offset and next of 3rd <p> should be same width");
|
|
|
|
// too big offset for the editors as array
|
|
tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 4 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
|
|
!checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
contenteditable.innerHTML = "<p>abc</p><p>def</p><p></p>";
|
|
// \n 0 123 4 567 8
|
|
// \r\n 01 234 56 789 0
|
|
|
|
description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
|
|
|
|
// "f"
|
|
f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
|
|
if (!checkQueryContentResult(f, description + "rect for 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(f.top, e.top, description + "'e' and 'f' should be at same top");
|
|
isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
|
|
is(f.height, e.height, description + "'e' and 'f' should be same height");
|
|
|
|
// 3rd <p> (can be computed with rect of 'f')
|
|
p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
|
|
is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
|
|
isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
|
|
|
|
// 3rd <p> as array
|
|
p3AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(p3AsArray, description + "3rd <p>'s line breaker as array") ||
|
|
!checkRectArray(p3AsArray, [p3], description + "query text rect array result of 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(p3_2, description + "rect for \\n of \\r\\n caused by 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
|
|
is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let p3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(p3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <p>") ||
|
|
!checkRectArray(p3_2AsArray, [p3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// next of 3rd <p>
|
|
let next_p3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
|
|
if (!checkQueryContentResult(next_p3, description + "rect for next of 3rd <p>")) {
|
|
return;
|
|
}
|
|
|
|
isGreaterThan(next_p3.top, d.top + d.height, description + "top of next of 3rd <p> should equal to or be bigger than bottom of 'd'");
|
|
isSimilarTo(next_p3.left, d.left, 2, description + "left of next of 3rd <p> should be at similar to left of 'd'");
|
|
isSimilarTo(next_p3.height, d.height, 2, description + "next of 3rd <p> and 'd' should be similar height");
|
|
|
|
// next of 3rd <p> as array
|
|
let next_p3AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
|
|
if (!checkQueryContentResult(next_p3AsArray, description + "next of 3rd <p> as array") ||
|
|
!checkRectArray(next_p3AsArray, [next_p3], description + "query text rect array result of next of 3rd <p> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// too big offset for the editor
|
|
tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
|
|
return;
|
|
}
|
|
|
|
is(tooBigOffset.top, next_p3.top, description + "too big offset and next of 3rd <p> should be at same top");
|
|
is(tooBigOffset.left, next_p3.left, description + "too big offset and next of 3rd <p> should be at same left");
|
|
is(tooBigOffset.height, next_p3.height, description + "too big offset and next of 3rd <p> should be same height");
|
|
is(tooBigOffset.width, next_p3.width, description + "too big offset and next of 3rd <p> should be same width");
|
|
|
|
// too big offset for the editors as array
|
|
tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
|
|
!checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
contenteditable.innerHTML = "abc<br>def";
|
|
// \n 0123 456
|
|
// \r\n 01234 567
|
|
|
|
description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
|
|
|
|
// "a"
|
|
a = synthesizeQueryTextRect(0, 1);
|
|
if (!checkQueryContentResult(a, description + "rect for 'a'")) {
|
|
return;
|
|
}
|
|
|
|
// "b"
|
|
b = synthesizeQueryTextRect(1, 1);
|
|
if (!checkQueryContentResult(b, description + "rect for 'b'")) {
|
|
return;
|
|
}
|
|
|
|
is(b.top, a.top, description + "'a' and 'b' should be at same top");
|
|
isSimilarTo(b.left, a.left + a.width, 2, description + "left of 'b' should be at similar to right of 'a'");
|
|
is(b.height, a.height, description + "'a' and 'b' should be same height");
|
|
|
|
// "c"
|
|
c = synthesizeQueryTextRect(2, 1);
|
|
if (!checkQueryContentResult(c, description + "rect for 'c'")) {
|
|
return;
|
|
}
|
|
|
|
is(c.top, b.top, description + "'b' and 'c' should be at same top");
|
|
isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
|
|
is(c.height, b.height, description + "'b' and 'c' should be same height");
|
|
|
|
// "abc" as array
|
|
abcAsArray = synthesizeQueryTextRectArray(0, 3);
|
|
if (!checkQueryContentResult(abcAsArray, description + "rect array for 'abc'") ||
|
|
!checkRectArray(abcAsArray, [a, b, c], description + "query text rect array result of 'abc' should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// <br> (can be computed with the rect of 'c')
|
|
br = synthesizeQueryTextRect(3, 1);
|
|
if (!checkQueryContentResult(br, description + "rect for <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br.top, c.top, description + "'c' and a line breaker caused by <br> should be at same top");
|
|
isSimilarTo(br.left, c.left + c.width, 2, description + "left of a line breaker caused by <br> should be at similar to right of 'c'");
|
|
is(br.height, c.height, description + "'c' and a line breaker caused by <br> should be same height");
|
|
|
|
// <br> as array
|
|
brAsArray = synthesizeQueryTextRectArray(3, 1);
|
|
if (!checkQueryContentResult(brAsArray, description + "<br>'s line breaker as array") ||
|
|
!checkRectArray(brAsArray, [br], description + "query text rect array result of <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let br_2 = synthesizeQueryTextRect(4, 1);
|
|
if (!checkQueryContentResult(br_2, description + "rect for \n of \r\n caused by <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
|
|
is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let br_2AsArray = synthesizeQueryTextRectArray(4, 1);
|
|
if (!checkQueryContentResult(br_2AsArray, description + "rect array for \\n of \\r\\n caused by <br>") ||
|
|
!checkRectArray(br_2AsArray, [br_2], description + "query text rect array result of \\n of \\r\\n caused by <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// "d"
|
|
d = synthesizeQueryTextRect(kLFLen + 3, 1);
|
|
if (!checkQueryContentResult(d, description + "rect for 'd'")) {
|
|
return;
|
|
}
|
|
|
|
isSimilarTo(d.top, a.top + a.height, 2, description + "top of 'd' should be at similar to bottom of 'a'");
|
|
is(d.left, a.left, description + "'a' and 'd' should be same at same left");
|
|
is(d.height, a.height, description + "'a' and 'd' should be same height");
|
|
|
|
// "e"
|
|
e = synthesizeQueryTextRect(kLFLen + 4, 1);
|
|
if (!checkQueryContentResult(e, description + "rect for 'e'")) {
|
|
return;
|
|
}
|
|
|
|
is(e.top, d.top, description + "'d' and 'd' should be at same top");
|
|
isSimilarTo(e.left, d.left + d.width, 2, description + "left of 'e' should be at similar to right of 'd'");
|
|
is(e.height, d.height, description + "'d' and 'e' should be same height");
|
|
|
|
// "f"
|
|
f = synthesizeQueryTextRect(kLFLen + 5, 1);
|
|
if (!checkQueryContentResult(f, description + "rect for 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(f.top, e.top, description + "'e' and 'f' should be at same top");
|
|
isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
|
|
is(f.height, e.height, description + "'e' and 'f' should be same height");
|
|
|
|
// "def" as array
|
|
defAsArray = synthesizeQueryTextRectArray(kLFLen + 3, 3);
|
|
if (!checkQueryContentResult(defAsArray, description + "rect array for 'def'") ||
|
|
!checkRectArray(defAsArray, [d, e, f], description + "query text rect array result of 'def' should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// next of "f" (can be computed with rect of 'f')
|
|
next_f = synthesizeQueryTextRect(kLFLen + 6, 1);
|
|
if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
|
|
isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
|
|
is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
|
|
|
|
// next of "f" as array
|
|
next_fAsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
|
|
if (!checkQueryContentResult(next_fAsArray, description + "rect array for next of 'f'") ||
|
|
!checkRectArray(next_fAsArray, [next_f], description + "query text rect array result of next of 'f' should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// too big offset for the editor
|
|
tooBigOffset = synthesizeQueryTextRect(kLFLen + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
|
|
return;
|
|
}
|
|
|
|
is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
|
|
is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
|
|
is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
|
|
is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
|
|
|
|
// too big offset for the editors as array
|
|
tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
|
|
!checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// Note that this case does not have an empty line at the end.
|
|
contenteditable.innerHTML = "abc<br>def<br>";
|
|
// \n 0123 4567
|
|
// \r\n 01234 56789
|
|
|
|
description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
|
|
|
|
// "f"
|
|
f = synthesizeQueryTextRect(kLFLen + 5, 1);
|
|
if (!checkQueryContentResult(f, description + "rect for 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(f.top, e.top, description + "'e' and 'f' should be at same top");
|
|
is(f.height, e.height, description + "'e' and 'f' should be same height");
|
|
isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
|
|
|
|
// 2nd <br> (can be computed with rect of 'f')
|
|
let br2 = synthesizeQueryTextRect(kLFLen + 6, 1);
|
|
if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
|
|
is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
|
|
isSimilarTo(br2.left, f.left + f.width, 2, description + "left of a line breaker caused by 2nd <br> should be similar to right of 'f'");
|
|
|
|
// 2nd <br> as array
|
|
let br2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(br2AsArray, description + "2nd <br>'s line breaker as array") ||
|
|
!checkRectArray(br2AsArray, [br2], description + "query text rect array result of 2nd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
|
|
if (!checkQueryContentResult(br2_2, description + "rect for \\n of \\r\\n caused by 2nd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
|
|
is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let br2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
|
|
if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <br>") ||
|
|
!checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// next of 2nd <br>
|
|
let next_br2 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(next_br2, description + "rect for next of 2nd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(next_br2.top, br2.top, description + "2nd <br> and next of 2nd <br> should be at same top");
|
|
is(next_br2.left, br2.left, description + "2nd <br> and next of 2nd <br> should be at same top");
|
|
is(next_br2.height, br2.height, description + "2nd <br> and next of 2nd <br> should be same height");
|
|
is(next_br2.width, br2.width, description + "2nd <br> and next of 2nd <br> should be same width");
|
|
|
|
// next of 2nd <br> as array
|
|
let next_br2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 6, 1);
|
|
if (!checkQueryContentResult(next_br2AsArray, description + "rect array for next of 2nd <br>") ||
|
|
!checkRectArray(next_br2AsArray, [next_br2], description + "query text rect array result of next of 2nd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// too big offset for the editor
|
|
tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
|
|
return;
|
|
}
|
|
|
|
is(tooBigOffset.top, next_br2.top, description + "too big offset and next of 2nd <br> should be at same top");
|
|
is(tooBigOffset.left, next_br2.left, description + "too big offset and next of 2nd <br> should be at same left");
|
|
is(tooBigOffset.height, next_br2.height, description + "too big offset and next of 2nd <br> should be same height");
|
|
is(tooBigOffset.width, next_br2.width, description + "too big offset and next of 2nd <br> should be same width");
|
|
|
|
// too big offset for the editors as array
|
|
tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset") ||
|
|
!checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
contenteditable.innerHTML = "abc<br>def<br><br>";
|
|
// \n 0123 4567 8
|
|
// \r\n 01234 56789 01
|
|
|
|
description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
|
|
|
|
// "f"
|
|
f = synthesizeQueryTextRect(kLFLen + 5, 1);
|
|
if (!checkQueryContentResult(f, description + "rect for 'f'")) {
|
|
return;
|
|
}
|
|
|
|
is(f.top, e.top, description + "'e' and 'f' should be at same top");
|
|
isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
|
|
is(f.height, e.height, description + "'e' and 'f' should be same height");
|
|
|
|
// 2nd <br>
|
|
br2 = synthesizeQueryTextRect(kLFLen + 6, 1);
|
|
if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
|
|
is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
|
|
ok(f.left < br2.left, description + "left of a line breaker caused by 2nd <br> should be bigger than left of 'f', f.left=" + f.left + ", br2.left=" + br2.left);
|
|
|
|
// 2nd <br> as array
|
|
br2AsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
|
|
if (!checkQueryContentResult(br2AsArray, description + "2nd <br>'s line breaker as array") ||
|
|
!checkRectArray(br2AsArray, [br2], description + "query text rect array result of 2nd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
|
|
if (!checkQueryContentResult(br2_2, description + "rect for \\n of \\r\\n caused by 2nd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
|
|
is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
|
|
is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let br2_2AsArray = synthesizeQueryTextRectArray(kLFLen + 7, 1);
|
|
if (!checkQueryContentResult(br2_2AsArray, description + "rect array for \\n of \\r\\n caused by 2nd <br>") ||
|
|
!checkRectArray(br2_2AsArray, [br2_2], description + "query text rect array result of \\n of \\r\\n caused by 2nd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 3rd <br>
|
|
let br3 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(br3, description + "rect for next of 3rd <br>")) {
|
|
return;
|
|
}
|
|
|
|
isSimilarTo(br3.top, d.top + d.height, 3, description + "top of next of 3rd <br> should at similar to bottom of 'd'");
|
|
isSimilarTo(br3.left, d.left, 2, description + "left of next of 3rd <br> should be at similar to left of 'd'");
|
|
isSimilarTo(br3.height, d.height, 2, description + "next of 3rd <br> and 'd' should be similar height");
|
|
|
|
// 3rd <br> as array
|
|
let br3AsArray = synthesizeQueryTextRectArray(kLFLen + 6, 1);
|
|
if (!checkQueryContentResult(br3AsArray, description + "3rd <br>'s line breaker as array") ||
|
|
!checkRectArray(br3AsArray, [br3], description + "query text rect array result of 3rd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
// \n of \r\n
|
|
let br3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(br3_2, description + "rect for \\n of \\r\\n caused by 3rd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(br3_2.top, br3.top, description + "'\\r' and '\\n' should be at same top");
|
|
is(br3_2.left, br3.left, description + "'\\r' and '\\n' should be at same left");
|
|
is(br3_2.height, br3.height, description + "'\\r' and '\\n' should be same height");
|
|
is(br3_2.width, br3.width, description + "'\\r' and '\\n' should be same width");
|
|
|
|
// \n of \r\n as array
|
|
let br3_2AsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
|
|
if (!checkQueryContentResult(br3_2AsArray, description + "rect array for \\n of \\r\\n caused by 3rd <br>") ||
|
|
!checkRectArray(br3_2AsArray, [br3_2], description + "query text rect array result of \\n of \\r\\n caused by 3rd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// next of 3rd <br>
|
|
let next_br3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
|
|
if (!checkQueryContentResult(next_br3, description + "rect for next of 3rd <br>")) {
|
|
return;
|
|
}
|
|
|
|
is(next_br3.top, br3.top, description + "3rd <br> and next of 3rd <br> should be at same top");
|
|
is(next_br3.left, br3.left, description + "3rd <br> and next of 3rd <br> should be at same left");
|
|
is(next_br3.height, br3.height, description + "3rd <br> and next of 3rd <br> should be same height");
|
|
is(next_br3.width, br3.width, description + "3rd <br> and next of 3rd <br> should be same width");
|
|
|
|
// next of 3rd <br> as array
|
|
let next_br3AsArray = synthesizeQueryTextRectArray(kLFLen * 3 + 6, 1);
|
|
if (!checkQueryContentResult(next_br3AsArray, description + "rect array for next of 3rd <br>") ||
|
|
!checkRectArray(next_br3AsArray, [next_br3], description + "query text rect array result of next of 3rd <br> should match with each query text rect result")) {
|
|
return;
|
|
}
|
|
|
|
// too big offset for the editor
|
|
tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
|
|
if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
|
|
return;
|
|
}
|
|
|
|
is(tooBigOffset.top, next_br3.top, description + "too big offset and next of 3rd <br> should be at same top");
|
|
is(tooBigOffset.left, next_br3.left, description + "too big offset and next of 3rd <br> should be at same left");
|
|
is(tooBigOffset.height, next_br3.height, description + "too big offset and next of 3rd <br> should be same height");
|
|
is(tooBigOffset.width, next_br3.width, description + "too big offset and next of 3rd <br> should be same width");
|
|
|
|
// too big offset for the editors as array
|
|
tooBigOffsetAsArray = synthesizeQueryTextRectArray(kLFLen * 2 + 7, 1);
|
|
if (checkQueryContentResult(tooBigOffsetAsArray, description + "rect array for too big offset")) {
|
|
checkRectArray(tooBigOffsetAsArray, [tooBigOffset], description + "query text rect array result with too big offset should match with each query text rect result");
|
|
}
|
|
|
|
if (!(function test_query_text_rects_across_invisible_text() {
|
|
contenteditable.innerHTML = "<div>\n<div>abc</div></div>";
|
|
// \n 0 1 2 345
|
|
// \r\n 01 2345 678
|
|
description = `runQueryTextRectInContentEditableTest: test_query_text_rects_across_invisible_text: "${
|
|
contenteditable.innerHTML.replace(/\n/g, "\\n")
|
|
}",`;
|
|
// rect of "a"
|
|
const rectA = synthesizeQueryTextRect(kLFLen * 3, 1);
|
|
if (!checkQueryContentResult(rectA, `${description} rect of "a"`)) {
|
|
return false;
|
|
}
|
|
const rectArrayFromStartToA = synthesizeQueryTextRectArray(0, kLFLen * 3 + 1);
|
|
if (!checkQueryContentResult(rectArrayFromStartToA, `${description} rect array from invisible text to "a"`)) {
|
|
return false;
|
|
}
|
|
const fromStartToARects = getRectArray(rectArrayFromStartToA);
|
|
if (!checkRect(
|
|
fromStartToARects[kLFLen * 3],
|
|
rectA,
|
|
`${description} rect for "a" in array should be same as the result of query only it`
|
|
)) {
|
|
return false;
|
|
}
|
|
return checkRect(
|
|
fromStartToARects[kLFLen * 2],
|
|
fromStartToARects[0],
|
|
`${description} rect for the linebreak in invisible text node should be same as first linebreak`
|
|
);
|
|
})()) {
|
|
return;
|
|
}
|
|
|
|
function test_query_text_rects_starting_from_invisible_text() {
|
|
contenteditable.innerHTML = "<div>\n<div>abc</div></div>";
|
|
// \n 0 1 2 345
|
|
// \r\n 01 2345 678
|
|
description = `runQueryTextRectInContentEditableTest: test_query_text_rects_starting_from_invisible_text: "${
|
|
contenteditable.innerHTML.replace(/\n/g, "\\n")
|
|
}",`;
|
|
// rect of "a"
|
|
const rectA = synthesizeQueryTextRect(kLFLen * 3, 1);
|
|
if (!checkQueryContentResult(rectA, `${description} rect of "a"`)) {
|
|
return false;
|
|
}
|
|
const rectArrayFromInvisibleToA = synthesizeQueryTextRectArray(kLFLen, kLFLen * 2 + 1);
|
|
if (!checkQueryContentResult(rectArrayFromInvisibleToA, `${description} rect array from invisible text to "a"`)) {
|
|
return false;
|
|
}
|
|
const fromInvisibleToARects = getRectArray(rectArrayFromInvisibleToA);
|
|
if (!checkRect(
|
|
fromInvisibleToARects[kLFLen * 2],
|
|
rectA,
|
|
`${description} rect for "a" in array should be same as the result of query only it`
|
|
)) {
|
|
return false;
|
|
}
|
|
// For now the rect of characters in invisible text node should be caret rect
|
|
// before the following line break. This is inconsistent from the result of
|
|
// the query text rect array event starting from the previous visible line
|
|
// break or character, but users anyway cannot insert text into the invisible
|
|
// text node only with user's operation. Therefore, this won't be problem
|
|
// in usual web apps.
|
|
const caretRectBeforeLineBreakBeforeA = fromInvisibleToARects[kLFLen];
|
|
return checkRect(
|
|
fromInvisibleToARects[0],
|
|
caretRectBeforeLineBreakBeforeA,
|
|
`${description} rect for the linebreak in invisible text node should be same as caret rect before the following visible linebreak before "a"`
|
|
);
|
|
}
|
|
if (!test_query_text_rects_starting_from_invisible_text()) {
|
|
return;
|
|
}
|
|
|
|
if (kLFLen > 1) {
|
|
function test_query_text_rects_starting_from_middle_of_invisible_linebreak() {
|
|
contenteditable.innerHTML = "<div>\n<div>abc</div></div>";
|
|
// \n 0 1 2 345
|
|
// \r\n 01 2345 678
|
|
description = `runQueryTextRectInContentEditableTest: test_query_text_rects_starting_from_middle_of_invisible_linebreak: "${
|
|
contenteditable.innerHTML.replace(/\n/g, "\\n")
|
|
}",`;
|
|
// rect of "a"
|
|
const rectA = synthesizeQueryTextRect(kLFLen * 3, 1);
|
|
if (!checkQueryContentResult(rectA, `${description} rect of "a"`)) {
|
|
return false;
|
|
}
|
|
const rectArrayFromInvisibleToA = synthesizeQueryTextRectArray(kLFLen + 1, 1 + kLFLen + 1);
|
|
if (!checkQueryContentResult(rectArrayFromInvisibleToA, `${description} rect array from invisible text to "a"`)) {
|
|
return false;
|
|
}
|
|
const fromInvisibleToARects = getRectArray(rectArrayFromInvisibleToA);
|
|
if (!checkRect(
|
|
fromInvisibleToARects[1 + kLFLen],
|
|
rectA,
|
|
`${description} rect for "a" in array should be same as the result of query only it`
|
|
)) {
|
|
return false;
|
|
}
|
|
// For now the rect of characters in invisible text node should be caret rect
|
|
// before the following line break. This is inconsistent from the result of
|
|
// the query text rect array event starting from the previous visible line
|
|
// break or character, but users anyway cannot insert text into the invisible
|
|
// text node only with user's operation. Therefore, this won't be problem
|
|
// in usual web apps.
|
|
const caretRectBeforeLineBreakBeforeA = fromInvisibleToARects[1];
|
|
return checkRect(
|
|
fromInvisibleToARects[0],
|
|
caretRectBeforeLineBreakBeforeA,
|
|
`${description} rect for the linebreak in invisible text node should be same as caret rect before the following visible linebreak before "a"`
|
|
);
|
|
}
|
|
if (!test_query_text_rects_starting_from_middle_of_invisible_linebreak()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
function test_query_text_rects_ending_with_invisible_text() {
|
|
contenteditable.innerHTML = "<div><div>abc</div>\n</div>";
|
|
// \n 0 1 234 5
|
|
// \r\n 01 23 456 78
|
|
description = `runQueryTextRectInContentEditableTest: test_query_text_rects_ending_with_invisible_text: "${
|
|
contenteditable.innerHTML.replace(/\n/g, "\\n")
|
|
}",`;
|
|
// rect of "c"
|
|
const rectC = synthesizeQueryTextRect(kLFLen * 2 + 2, 1);
|
|
if (!checkQueryContentResult(rectC, `${description} rect of "c"`)) {
|
|
return false;
|
|
}
|
|
const rectArrayFromCToInvisible = synthesizeQueryTextRectArray(kLFLen * 2 + 2, 1 + kLFLen);
|
|
if (!checkQueryContentResult(rectArrayFromCToInvisible, `${description} rect array from "c" to invisible linebreak`)) {
|
|
return false;
|
|
}
|
|
const fromCToInvisibleRects = getRectArray(rectArrayFromCToInvisible);
|
|
if (!checkRect(
|
|
fromCToInvisibleRects[0],
|
|
rectC,
|
|
`${description} rect for "c" in array should be same as the result of query only it`
|
|
)) {
|
|
return false;
|
|
}
|
|
const caretRectAfterC = {
|
|
left: rectC.left + rectC.width,
|
|
top: rectC.top,
|
|
width: 1,
|
|
height: rectC.height,
|
|
};
|
|
return checkRectFuzzy(
|
|
fromCToInvisibleRects[1],
|
|
caretRectAfterC,
|
|
{
|
|
left: 1,
|
|
top: 0,
|
|
width: 0,
|
|
height: 0,
|
|
},
|
|
`${description} rect for the linebreak in invisible text node should be same as caret rect after "c"`
|
|
);
|
|
}
|
|
if (!test_query_text_rects_ending_with_invisible_text()) {
|
|
// eslint-disable-next-line no-useless-return
|
|
return;
|
|
}
|
|
}
|
|
|
|
function runCharAtPointTest(aFocusedEditor, aTargetName)
|
|
{
|
|
aFocusedEditor.value = "This is a test of the\nContent Events";
|
|
// 012345678901234567890 12345678901234
|
|
// 0 1 2 3
|
|
|
|
aFocusedEditor.focus();
|
|
|
|
const kNone = -1;
|
|
const kTestingOffset = [ 0, 10, 20, 21 + kLFLen, 34 + kLFLen];
|
|
const kLeftSideOffset = [ kNone, 9, 19, kNone, 33 + kLFLen];
|
|
const kRightSideOffset = [ 1, 11, kNone, 22 + kLFLen, kNone];
|
|
const kLeftTentativeCaretOffset = [ 0, 10, 20, 21 + kLFLen, 34 + kLFLen];
|
|
const kRightTentativeCaretOffset = [ 1, 11, 21, 22 + kLFLen, 35 + kLFLen];
|
|
|
|
let editorRect = synthesizeQueryEditorRect();
|
|
if (!checkQueryContentResult(editorRect,
|
|
"runCharAtPointTest (" + aTargetName + "): editorRect")) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < kTestingOffset.length; i++) {
|
|
let textRect = synthesizeQueryTextRect(kTestingOffset[i], 1);
|
|
if (!checkQueryContentResult(textRect,
|
|
"runCharAtPointTest (" + aTargetName + "): textRect: i=" + i)) {
|
|
continue;
|
|
}
|
|
|
|
checkRectContainsRect(textRect, editorRect,
|
|
"runCharAtPointTest (" + aTargetName +
|
|
"): the text rect isn't in the editor");
|
|
|
|
// Test #1, getting same character rect by the point near the top-left.
|
|
let charAtPt1 = synthesizeCharAtPoint(textRect.left + 1,
|
|
textRect.top + 1);
|
|
if (checkQueryContentResult(charAtPt1,
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt1: i=" + i)) {
|
|
ok(!charAtPt1.notFound,
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt1 isn't found: i=" + i);
|
|
if (!charAtPt1.notFound) {
|
|
is(charAtPt1.offset, kTestingOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt1 offset is wrong: i=" + i);
|
|
checkRect(charAtPt1, textRect, "runCharAtPointTest (" + aTargetName +
|
|
"): charAtPt1 left is wrong: i=" + i);
|
|
}
|
|
ok(!charAtPt1.tentativeCaretOffsetNotFound,
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt1 isn't found: i=" + i);
|
|
if (!charAtPt1.tentativeCaretOffsetNotFound) {
|
|
is(charAtPt1.tentativeCaretOffset, kLeftTentativeCaretOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt1 is wrong: i=" + i);
|
|
}
|
|
}
|
|
|
|
// Test #2, getting same character rect by the point near the bottom-right.
|
|
let charAtPt2 = synthesizeCharAtPoint(textRect.left + textRect.width - 2,
|
|
textRect.top + textRect.height - 2);
|
|
if (checkQueryContentResult(charAtPt2,
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt2: i=" + i)) {
|
|
ok(!charAtPt2.notFound,
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt2 isn't found: i=" + i);
|
|
if (!charAtPt2.notFound) {
|
|
is(charAtPt2.offset, kTestingOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt2 offset is wrong: i=" + i);
|
|
checkRect(charAtPt2, textRect, "runCharAtPointTest (" + aTargetName +
|
|
"): charAtPt1 left is wrong: i=" + i);
|
|
}
|
|
ok(!charAtPt2.tentativeCaretOffsetNotFound,
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt2 isn't found: i=" + i);
|
|
if (!charAtPt2.tentativeCaretOffsetNotFound) {
|
|
is(charAtPt2.tentativeCaretOffset, kRightTentativeCaretOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt2 is wrong: i=" + i);
|
|
}
|
|
}
|
|
|
|
// Test #3, getting left character offset.
|
|
let charAtPt3 = synthesizeCharAtPoint(textRect.left - 2,
|
|
textRect.top + 1);
|
|
if (checkQueryContentResult(charAtPt3,
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt3: i=" + i)) {
|
|
is(charAtPt3.notFound, kLeftSideOffset[i] == kNone,
|
|
kLeftSideOffset[i] == kNone ?
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt3 is found: i=" + i :
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt3 isn't found: i=" + i);
|
|
if (!charAtPt3.notFound) {
|
|
is(charAtPt3.offset, kLeftSideOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt3 offset is wrong: i=" + i);
|
|
}
|
|
if (kLeftSideOffset[i] == kNone) {
|
|
// There may be no enough padding-left (depends on platform)
|
|
todo(false,
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt3 isn't tested: i=" + i);
|
|
} else {
|
|
ok(!charAtPt3.tentativeCaretOffsetNotFound,
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt3 isn't found: i=" + i);
|
|
if (!charAtPt3.tentativeCaretOffsetNotFound) {
|
|
is(charAtPt3.tentativeCaretOffset, kLeftTentativeCaretOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt3 is wrong: i=" + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test #4, getting right character offset.
|
|
let charAtPt4 = synthesizeCharAtPoint(textRect.left + textRect.width + 1,
|
|
textRect.top + textRect.height - 2);
|
|
if (checkQueryContentResult(charAtPt4,
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt4: i=" + i)) {
|
|
is(charAtPt4.notFound, kRightSideOffset[i] == kNone,
|
|
kRightSideOffset[i] == kNone ?
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt4 is found: i=" + i :
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt4 isn't found: i=" + i);
|
|
if (!charAtPt4.notFound) {
|
|
is(charAtPt4.offset, kRightSideOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): charAtPt4 offset is wrong: i=" + i);
|
|
}
|
|
ok(!charAtPt4.tentativeCaretOffsetNotFound,
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt4 isn't found: i=" + i);
|
|
if (!charAtPt4.tentativeCaretOffsetNotFound) {
|
|
is(charAtPt4.tentativeCaretOffset, kRightTentativeCaretOffset[i],
|
|
"runCharAtPointTest (" + aTargetName + "): tentative caret offset for charAtPt4 is wrong: i=" + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function runCharAtPointAtOutsideTest()
|
|
{
|
|
textarea.focus();
|
|
textarea.value = "some text";
|
|
let editorRect = synthesizeQueryEditorRect();
|
|
if (!checkQueryContentResult(editorRect,
|
|
"runCharAtPointAtOutsideTest: editorRect")) {
|
|
return;
|
|
}
|
|
// Check on a text node which is at the outside of editor.
|
|
let charAtPt = synthesizeCharAtPoint(editorRect.left + 20,
|
|
editorRect.top - 10);
|
|
if (checkQueryContentResult(charAtPt,
|
|
"runCharAtPointAtOutsideTest: charAtPt")) {
|
|
ok(charAtPt.notFound,
|
|
"runCharAtPointAtOutsideTest: charAtPt is found on outside of editor");
|
|
ok(charAtPt.tentativeCaretOffsetNotFound,
|
|
"runCharAtPointAtOutsideTest: tentative caret offset for charAtPt is found on outside of editor");
|
|
}
|
|
}
|
|
|
|
async function runSetSelectionEventTest()
|
|
{
|
|
contenteditable.focus();
|
|
|
|
const selection = windowOfContenteditable.getSelection();
|
|
|
|
// #1
|
|
contenteditable.innerHTML = "abc<br>def";
|
|
|
|
await synthesizeSelectionSet(0, 100);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node of the editor");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of children");
|
|
checkSelection(0, "abc" + kLF + "def", "runSetSelectionEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(2, 2 + kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 2,
|
|
"runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 2");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(2, "c" + kLF + "d", "runSetSelectionEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(1, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node");
|
|
checkSelection(1, "bc", "runSetSelectionEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the first text node");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 2,
|
|
"runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the index of the last text node");
|
|
checkSelection(3, kLF, "runSetSelectionEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(6+kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, contenteditable.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.anchorOffset, contenteditable.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(6 + kLFLen, "", "runSetSelectionEventTest #1 (6+kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(100, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node of the editor");
|
|
is(selection.anchorOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of children");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node of the editor");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of children");
|
|
checkSelection(6 + kLFLen, "", "runSetSelectionEventTest #1 (100, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #2
|
|
contenteditable.innerHTML = "<p>a<b>b</b>c</p><p>def</p>";
|
|
|
|
await synthesizeSelectionSet(kLFLen, 4+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first <p> node");
|
|
is(selection.focusNode, contenteditable.lastChild.firstChild,
|
|
"runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the second <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(kLFLen, "abc" + kLF + "d", "runSetSelectionEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first <p> node");
|
|
is(selection.focusNode, contenteditable.firstChild.childNodes.item(1).firstChild,
|
|
"runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <b> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.item(1).firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node in the <b> node");
|
|
checkSelection(kLFLen, "ab", "runSetSelectionEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(1+kLFLen, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node in the first <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node in the first <p> node");
|
|
checkSelection(1+kLFLen, "bc", "runSetSelectionEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(2+kLFLen, 2+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.childNodes.item(1).firstChild,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <b> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.childNodes.item(1).firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node in the <b> node");
|
|
is(selection.focusNode, contenteditable.lastChild.firstChild,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the last <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(2+kLFLen, "c" + kLF + "d", "runSetSelectionEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen*2, 1);
|
|
is(selection.anchorNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the second <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the second <p> node");
|
|
is(selection.focusNode, contenteditable.lastChild.firstChild,
|
|
"runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the second <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(3+kLFLen*2, "d", "runSetSelectionEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #2 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first <p> node");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the first <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, kLF, "runSetSelectionEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(2+kLFLen, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.childNodes.item(1).firstChild,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node of the <b> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.childNodes.item(1).firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node of the first <b> node");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(2+kLFLen, "c" + kLF, "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node of the first <p> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node of the first <p> node");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(3+kLFLen, kLF, "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node of the first <p> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node of the first <p> node");
|
|
is(selection.focusNode, contenteditable.lastChild.firstChild,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node of the second <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(3+kLFLen, kLF + "d", "runSetSelectionEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #3
|
|
contenteditable.innerHTML = "<div>abc<p>def</p></div>";
|
|
|
|
await synthesizeSelectionSet(1+kLFLen, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the first text node");
|
|
checkSelection(1+kLFLen, "bc", "runSetSelectionEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(1+kLFLen, 3+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(1+kLFLen, "bc" + kLF + "d", "runSetSelectionEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the first text node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the first text node");
|
|
checkSelection(3+kLFLen, "", "runSetSelectionEventTest #3 (3+kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 6+kLFLen*2);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(0, kLF + "abc" + kLF + "def", "runSetSelectionEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 100);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(0, kLF + "abc" + kLF + "def", "runSetSelectionEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(4+kLFLen*2, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(4+kLFLen*2, 100);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(6+kLFLen*2, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #3 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(6+kLFLen*2, 1);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.lastChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the first text node");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <div> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, kLF, "runSetSelectionEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the first text node of the <div> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(0, kLF + "a", "runSetSelectionEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(2+kLFLen, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node of the <div> node");
|
|
is(selection.anchorOffset, 2,
|
|
"runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 2");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(2+kLFLen, "c" + kLF, "runSetSelectionEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node of the <div> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node of the <div> node");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(3+kLFLen, kLF, "runSetSelectionEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node of the <div> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node of the <div> node");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild.firstChild,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node of the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(3+kLFLen, kLF + "d", "runSetSelectionEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #4
|
|
contenteditable.innerHTML = "<div><p>abc</p>def</div>";
|
|
|
|
await synthesizeSelectionSet(1+kLFLen*2, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <p> node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node in the <p> node");
|
|
checkSelection(1+kLFLen*2, "bc", "runSetSelectionEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(1+kLFLen*2, 3);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <p> node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(1+kLFLen*2, "bcd", "runSetSelectionEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(3+kLFLen*2, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node in the <p> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the text node in the <p> node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.firstChild.firstChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the text node in the <p> node");
|
|
checkSelection(3+kLFLen*2, "", "runSetSelectionEventTest #4 (3+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 6+kLFLen*2);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(0, kLF + kLF + "abcdef", "runSetSelectionEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 100);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(0, kLF + kLF + "abcdef", "runSetSelectionEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(4+kLFLen*2, 2);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(4+kLFLen*2, 100);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(4+kLFLen*2, "ef", "runSetSelectionEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(6+kLFLen*2, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
|
|
is(selection.focusNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #4 (6+kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(6+kLFLen*2, 1);
|
|
is(selection.anchorNode, contenteditable.firstChild.lastChild,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the last text node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the length of the last text node");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(6+kLFLen*2, "", "runSetSelectionEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <div> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, kLF, "runSetSelectionEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen*2);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, kLF + kLF, "runSetSelectionEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 1+kLFLen*2);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <div> node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(0, kLF + kLF + "a", "runSetSelectionEventTest #4 (0, 1+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <div> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <div> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #4 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <div> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(kLFLen, kLF, "runSetSelectionEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <div> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild.firstChild,
|
|
"runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node in the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(kLFLen, kLF +"a", "runSetSelectionEventTest #4 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #5
|
|
contenteditable.innerHTML = "<br>";
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #5 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(0, kLF, "runSetSelectionEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #5 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 1);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #6
|
|
contenteditable.innerHTML = "<p><br></p>";
|
|
|
|
await synthesizeSelectionSet(kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
|
|
checkSelection(kLFLen, kLF, "runSetSelectionEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen*2, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the <p>'s children");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
|
|
checkSelection(kLFLen*2, "", "runSetSelectionEventTest #6 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen*2, 1);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(kLFLen*2, "", "runSetSelectionEventTest #6 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, kLF, "runSetSelectionEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen*2);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
|
|
checkSelection(0, kLF + kLF, "runSetSelectionEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #6 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #7
|
|
contenteditable.innerHTML = "<br><br>";
|
|
|
|
await synthesizeSelectionSet(0, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(0, kLF, "runSetSelectionEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, kLFLen * 2);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(0, kLF + kLF, "runSetSelectionEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #7 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (kLFLen, kLFLen) selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(kLFLen, kLF, "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen * 2, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the root's children");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(kLFLen * 2, "", "runSetSelectionEventTest #7 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #8
|
|
contenteditable.innerHTML = "<p><br><br></p>";
|
|
|
|
await synthesizeSelectionSet(kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(kLFLen, kLF, "runSetSelectionEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, kLFLen * 2);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
|
|
checkSelection(kLFLen, kLF + kLF, "runSetSelectionEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen*2, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 1");
|
|
checkSelection(kLFLen*2, "", "runSetSelectionEventTest #8 (kLFLen*2, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen*2, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, kLFLen) selection focus node should be the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
|
|
checkSelection(kLFLen*2, kLF, "runSetSelectionEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen*3, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the <p> node");
|
|
is(selection.anchorOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the count of the <p>'s children");
|
|
is(selection.focusNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, contenteditable.firstChild.childNodes.length,
|
|
"runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the <p>'s children");
|
|
checkSelection(kLFLen*3, "", "runSetSelectionEventTest #8 (kLFLen*3, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #9 (ContentEventHandler cannot distinguish if <p> can have children, so, the result is same as case #5, "<br>")
|
|
contenteditable.innerHTML = "<p></p>";
|
|
|
|
await synthesizeSelectionSet(kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node + 1");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the <p> node");
|
|
is(selection.focusOffset, 1,
|
|
"runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be the index of the <p> node + 1");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(kLFLen, 1);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be the index of the <p> node + 1");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #9 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #9 (kLFLen, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(kLFLen, "", "runSetSelectionEventTest #9 (kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #10
|
|
contenteditable.innerHTML = "";
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #10 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 1);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #11
|
|
contenteditable.innerHTML = "<span></span><i><u></u></i>";
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(0, 1);
|
|
is(selection.anchorNode, contenteditable,
|
|
"runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable,
|
|
"runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
|
|
is(selection.focusOffset, contenteditable.childNodes.length,
|
|
"runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
|
|
checkSelection(0, "", "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #12
|
|
contenteditable.innerHTML = "<span>abc</span><i><u></u></i>";
|
|
selection.selectAllChildren(contenteditable);
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.firstChild.firstChild,
|
|
"runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #12 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #13
|
|
contenteditable.innerHTML = "<span></span><i>abc<u></u></i>";
|
|
selection.selectAllChildren(contenteditable);
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable.childNodes.item(1).firstChild,
|
|
"runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.childNodes.item(1).firstChild,
|
|
"runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #13 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #14
|
|
contenteditable.innerHTML = "<span></span><i><u>abc</u></i>";
|
|
selection.selectAllChildren(contenteditable);
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable.childNodes.item(1).firstChild.firstChild,
|
|
"runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.childNodes.item(1).firstChild.firstChild,
|
|
"runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #14 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #15
|
|
contenteditable.innerHTML = "<span></span><i><u></u>abc</i>";
|
|
selection.selectAllChildren(contenteditable);
|
|
|
|
await synthesizeSelectionSet(0, 0);
|
|
is(selection.anchorNode, contenteditable.childNodes.item(1).lastChild,
|
|
"runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the text node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.childNodes.item(1).lastChild,
|
|
"runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the text node");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(0, "", "runSetSelectionEventTest #15 (0, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #16
|
|
contenteditable.innerHTML = "a<blink>b</blink>c";
|
|
await synthesizeSelectionSet(0, 3);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first text node");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection focus node should be the last text node");
|
|
is(selection.focusOffset, contenteditable.lastChild.wholeText.length,
|
|
"runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\": selection focus offset should be the length of the last text node");
|
|
checkSelection(0, "abc", "runSetSelectionEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #17 (bug 1319660 - incorrect adjustment of content iterator last node)
|
|
contenteditable.innerHTML = "<div>a</div><div><br></div>";
|
|
|
|
await synthesizeSelectionSet(kLFLen, 1+kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <div> element");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <div> element");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(kLFLen, "a" + kLF, "runSetSelectionEventTest #17 (kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
await synthesizeSelectionSet(1+2*kLFLen, 0);
|
|
is(selection.anchorNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the second <div> element");
|
|
is(selection.anchorOffset, 0,
|
|
"runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <div> element");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(1+2*kLFLen, "", "runSetSelectionEventTest #17 (1+2*kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #18 (bug 1319660 - content iterator start node regression)
|
|
contenteditable.innerHTML = "<div><br></div><div><br></div>";
|
|
|
|
await synthesizeSelectionSet(2*kLFLen, kLFLen);
|
|
is(selection.anchorNode, contenteditable.firstChild,
|
|
"runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor node should be the first <div> element");
|
|
is(selection.anchorOffset, 1,
|
|
"runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 1");
|
|
is(selection.focusNode, contenteditable.lastChild,
|
|
"runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus node should be the second <div> element");
|
|
is(selection.focusOffset, 0,
|
|
"runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
|
|
checkSelection(2*kLFLen, kLF, "runSetSelectionEventTest #18 (2*kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
}
|
|
|
|
function runQueryTextContentEventTest()
|
|
{
|
|
contenteditable.focus();
|
|
|
|
let result;
|
|
|
|
// #1
|
|
contenteditable.innerHTML = "abc<br>def";
|
|
|
|
result = synthesizeQueryTextContent(0, 6 + kLFLen);
|
|
is(result.text, "abc" + kLF + "def", "runQueryTextContentEventTest #1 (0, 6+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 100);
|
|
is(result.text, "abc" + kLF + "def", "runQueryTextContentEventTest #1 (0, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(2, 2 + kLFLen);
|
|
is(result.text, "c" + kLF + "d", "runQueryTextContentEventTest #1 (2, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(1, 2);
|
|
is(result.text, "bc", "runQueryTextContentEventTest #1 (1, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #1 (3, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(6 + kLFLen, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #1 (6 + kLFLen, 0), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #2
|
|
contenteditable.innerHTML = "<p>a<b>b</b>c</p><p>def</p>";
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, 4+kLFLen);
|
|
is(result.text, "abc" + kLF + "d", "runQueryTextContentEventTest #2 (kLFLen, 4+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, 2);
|
|
is(result.text, "ab", "runQueryTextContentEventTest #2 (kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(1+kLFLen, 2);
|
|
is(result.text, "bc", "runQueryTextContentEventTest #2 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(2+kLFLen, 2+kLFLen);
|
|
is(result.text, "c" + kLF + "d", "runQueryTextContentEventTest #2 (2+kLFLen, 2+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen*2, 1);
|
|
is(result.text, "d", "runQueryTextContentEventTest #2 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #2 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(2+kLFLen, 1+kLFLen);
|
|
is(result.text, "c" + kLF, "runQueryTextContentEventTest #2 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #2 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen, 1+kLFLen);
|
|
is(result.text, kLF + "d", "runQueryTextContentEventTest #2 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #3
|
|
contenteditable.innerHTML = "<div>abc<p>def</p></div>";
|
|
|
|
result = synthesizeQueryTextContent(1+kLFLen, 2);
|
|
is(result.text, "bc", "runQueryTextContentEventTest #3 (1+kLFLen, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(1+kLFLen, 3+kLFLen);
|
|
is(result.text, "bc" + kLF + "d", "runQueryTextContentEventTest #3 (1+kLFLen, 3+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen*2, 1);
|
|
is(result.text, "d", "runQueryTextContentEventTest #3 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 6+kLFLen*2);
|
|
is(result.text, kLF + "abc" + kLF + "def", "runQueryTextContentEventTest #3 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 100);
|
|
is(result.text, kLF + "abc" + kLF + "def", "runQueryTextContentEventTest #3 (0, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(4+kLFLen*2, 2);
|
|
is(result.text, "ef", "runQueryTextContentEventTest #3 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(4+kLFLen*2, 100);
|
|
is(result.text, "ef", "runQueryTextContentEventTest #3 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(6+kLFLen*2, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #3 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #3 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 1+kLFLen);
|
|
is(result.text, kLF + "a", "runQueryTextContentEventTest #3 (0, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(2+kLFLen, 1+kLFLen);
|
|
is(result.text, "c" + kLF, "runQueryTextContentEventTest #3 (2+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #3 (3+kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen, 1+kLFLen);
|
|
is(result.text, kLF + "d", "runQueryTextContentEventTest #3 (3+kLFLen, 1+kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #4
|
|
contenteditable.innerHTML = "<div><p>abc</p>def</div>";
|
|
|
|
result = synthesizeQueryTextContent(1+kLFLen*2, 2);
|
|
is(result.text, "bc", "runQueryTextContentEventTest #4 (1+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(1+kLFLen*2, 3);
|
|
is(result.text, "bcd", "runQueryTextContentEventTest #4 (1+kLFLen*2, 3), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(3+kLFLen*2, 1);
|
|
is(result.text, "d", "runQueryTextContentEventTest #4 (3+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 6+kLFLen*2);
|
|
is(result.text, kLF + kLF + "abcdef", "runQueryTextContentEventTest #4 (0, 6+kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 100);
|
|
is(result.text, kLF + kLF + "abcdef", "runQueryTextContentEventTest #4 (0, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(4+kLFLen*2, 2);
|
|
is(result.text, "ef", "runQueryTextContentEventTest #4 (4+kLFLen*2, 2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(4+kLFLen*2, 100);
|
|
is(result.text, "ef", "runQueryTextContentEventTest #4 (4+kLFLen*2, 100), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(6+kLFLen*2, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #4 (6+kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #4 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen*2);
|
|
is(result.text, kLF + kLF, "runQueryTextContentEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, 1+kLFLen*2);
|
|
is(result.text, kLF + kLF + "a", "runQueryTextContentEventTest #4 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, 1+kLFLen);
|
|
is(result.text, kLF + "a", "runQueryTextContentEventTest #4 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #5
|
|
contenteditable.innerHTML = "<br>";
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #5 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #5 (kLFLen, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #6
|
|
contenteditable.innerHTML = "<p><br></p>";
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #6 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen*2, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #5 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #6 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen*2);
|
|
is(result.text, kLF + kLF, "runQueryTextContentEventTest #6 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #7
|
|
contenteditable.innerHTML = "<br><br>";
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #7 (0, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(0, kLFLen * 2);
|
|
is(result.text, kLF + kLF, "runQueryTextContentEventTest #7 (0, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #7 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen * 2, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #7 (kLFLen*2, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #8
|
|
contenteditable.innerHTML = "<p><br><br></p>";
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #8 (kLFLen, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen, kLFLen * 2);
|
|
is(result.text, kLF + kLF, "runQueryTextContentEventTest #8 (kLFLen, kLFLen*2), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen*2, kLFLen);
|
|
is(result.text, kLF, "runQueryTextContentEventTest #8 (kLFLen*2, kLFLen), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
result = synthesizeQueryTextContent(kLFLen*3, 1);
|
|
is(result.text, "", "runQueryTextContentEventTest #8 (kLFLen*3, 1), \"" + contenteditable.innerHTML + "\"");
|
|
|
|
// #16
|
|
contenteditable.innerHTML = "a<blink>b</blink>c";
|
|
|
|
result = synthesizeQueryTextContent(0, 3);
|
|
is(result.text, "abc", "runQueryTextContentEventTest #16 (0, 3), \"" + contenteditable.innerHTML + "\"");
|
|
}
|
|
|
|
function runQuerySelectionEventTest()
|
|
{
|
|
contenteditable.focus();
|
|
|
|
let selection = windowOfContenteditable.getSelection();
|
|
|
|
// #1
|
|
contenteditable.innerHTML = "<br/>a";
|
|
selection.setBaseAndExtent(
|
|
contenteditable.firstChild,
|
|
0,
|
|
contenteditable.lastChild,
|
|
1
|
|
);
|
|
checkSelection(
|
|
0,
|
|
`${kLF}a`,
|
|
`runQuerySelectionEventTest #1, "${contenteditable.innerHTML}"`
|
|
);
|
|
|
|
// #2
|
|
contenteditable.innerHTML = "<p></p><p>abc</p>";
|
|
selection.setBaseAndExtent(
|
|
contenteditable.firstChild,
|
|
0,
|
|
contenteditable.lastChild.firstChild,
|
|
1
|
|
);
|
|
checkSelection(
|
|
kLFLen,
|
|
`${kLF}a`,
|
|
`runQuerySelectionEventTest #2, "${contenteditable.innerHTML}"`
|
|
);
|
|
|
|
// #3
|
|
contenteditable.innerHTML = "<p>abc</p><p>def</p>";
|
|
selection.setBaseAndExtent(
|
|
contenteditable.firstChild,
|
|
0,
|
|
contenteditable.lastChild.firstChild,
|
|
1
|
|
);
|
|
checkSelection(
|
|
kLFLen,
|
|
`abc${kLF}d`,
|
|
`runQuerySelectionEventTest #3, "${contenteditable.innerHTML}"`
|
|
);
|
|
|
|
// #4
|
|
contenteditable.innerHTML = "<p>abc</p>";
|
|
selection.removeAllRanges();
|
|
checkSelection(
|
|
null,
|
|
null,
|
|
`runQuerySelectionEventTest #4, "${contenteditable.innerHTML}"`
|
|
);
|
|
}
|
|
|
|
function runQueryIMESelectionTest()
|
|
{
|
|
textarea.focus();
|
|
textarea.value = "before after";
|
|
let startoffset = textarea.selectionStart = textarea.selectionEnd = "before ".length;
|
|
|
|
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
|
|
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
|
|
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: before starting composition") ||
|
|
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: before starting composition")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "a",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
if (!checkIMESelection("RawClause", true, startoffset, "a", "runQueryIMESelectionTest: inputting raw text") ||
|
|
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text") ||
|
|
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text") ||
|
|
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: inputting raw text")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "abcdefgh",
|
|
"clauses":
|
|
[
|
|
{ "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 8, "length": 0 }
|
|
});
|
|
|
|
if (!checkIMESelection("RawClause", true, startoffset, "abcdefgh", "runQueryIMESelectionTest: updating raw text") ||
|
|
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: updating raw text") ||
|
|
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: updating raw text") ||
|
|
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: updating raw text")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABCDEFGH",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: starting to convert") ||
|
|
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: starting to convert") ||
|
|
!checkIMESelection("ConvertedClause", true, startoffset + 2, "CDE", "runQueryIMESelectionTest: starting to convert") ||
|
|
!checkIMESelection("SelectedClause", true, startoffset, "AB", "runQueryIMESelectionTest: starting to convert")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABCDEFGH",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 }
|
|
});
|
|
|
|
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: changing selected clause") ||
|
|
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: changing selected clause") ||
|
|
!checkIMESelection("ConvertedClause", true, startoffset, "AB", "runQueryIMESelectionTest: changing selected clause") ||
|
|
!checkIMESelection("SelectedClause", true, startoffset + 2, "CDE", "runQueryIMESelectionTest: changing selected clause")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
if (!checkIMESelection("RawClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
|
|
!checkIMESelection("SelectedRawClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
|
|
!checkIMESelection("ConvertedClause", false, 0, "", "runQueryIMESelectionTest: after committing composition") ||
|
|
!checkIMESelection("SelectedClause", false, 0, "", "runQueryIMESelectionTest: after committing composition")) {
|
|
return;
|
|
}
|
|
|
|
startoffset = textarea.selectionStart;
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "abcdefgh",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
]
|
|
},
|
|
"caret": { "start": 8, "length": 0 }
|
|
});
|
|
|
|
if (!checkIMESelection("RawClause", true, startoffset, "a", "runQueryIMESelectionTest: unrealistic testcase") ||
|
|
!checkIMESelection("SelectedRawClause", true, startoffset + 1, "b", "runQueryIMESelectionTest: unrealistic testcase") ||
|
|
!checkIMESelection("ConvertedClause", true, startoffset + 2, "c", "runQueryIMESelectionTest: unrealistic testcase") ||
|
|
!checkIMESelection("SelectedClause", true, startoffset + 3, "d", "runQueryIMESelectionTest: unrealistic testcase")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
}
|
|
|
|
function runQueryPasswordTest() {
|
|
function checkRange(aOffset, aLength, aExpectedResult, aDescription) {
|
|
password.focus();
|
|
let result = synthesizeQueryTextContent(aOffset, aLength);
|
|
is(result.text, aExpectedResult,
|
|
`${aDescription}: synthesizeQueryTextContent(${aOffset}, ${aLength})`);
|
|
password.setSelectionRange(aOffset, aOffset + aLength);
|
|
result = synthesizeQuerySelectedText();
|
|
is(result.text, aExpectedResult,
|
|
`${aDescription}: synthesizeQuerySelectedText(${aOffset}, ${aLength})`);
|
|
}
|
|
|
|
let editor = password.editor;
|
|
const kMask = editor.passwordMask;
|
|
password.value = "abcdef";
|
|
|
|
editor.mask();
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range is not specified #1");
|
|
checkRange(0, 3, `${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range is not specified #2");
|
|
checkRange(3, 3, `${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range is not specified #3");
|
|
checkRange(2, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range is not specified #4");
|
|
|
|
editor.unmask(0, 6);
|
|
checkRange(0, 6, "abcdef",
|
|
"runQueryPasswordTest: unmasked range 0-6 #1");
|
|
checkRange(0, 3, "abc",
|
|
"runQueryPasswordTest: unmasked range 0-6 #2");
|
|
checkRange(3, 3, "def",
|
|
"runQueryPasswordTest: unmasked range 0-6 #3");
|
|
checkRange(2, 2, "cd",
|
|
"runQueryPasswordTest: unmasked range 0-6 #4");
|
|
|
|
editor.unmask(0, 3);
|
|
checkRange(0, 6, `abc${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 0-3 #1");
|
|
checkRange(0, 3, "abc",
|
|
"runQueryPasswordTest: unmasked range 0-3 #2");
|
|
checkRange(3, 3, `${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 0-3 #3");
|
|
checkRange(2, 2, `c${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 0-3 #4");
|
|
|
|
editor.unmask(3, 6);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}def`,
|
|
"runQueryPasswordTest: unmasked range 3-6 #1");
|
|
checkRange(0, 3, `${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 3-6 #2");
|
|
checkRange(3, 3, `def`,
|
|
"runQueryPasswordTest: unmasked range 3-6 #3");
|
|
checkRange(2, 2, `${kMask}d`,
|
|
"runQueryPasswordTest: unmasked range 3-6 #4");
|
|
|
|
editor.unmask(2, 4);
|
|
checkRange(0, 6, `${kMask}${kMask}cd${kMask}${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 3-4 #1");
|
|
checkRange(1, 2, `${kMask}c`,
|
|
"runQueryPasswordTest: unmasked range 3-4 #2");
|
|
checkRange(1, 3, `${kMask}cd`,
|
|
"runQueryPasswordTest: unmasked range 3-4 #3");
|
|
checkRange(1, 4, `${kMask}cd${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 3-4 #4");
|
|
checkRange(2, 2, "cd",
|
|
"runQueryPasswordTest: unmasked range 3-4 #5");
|
|
checkRange(2, 3, `cd${kMask}`,
|
|
"runQueryPasswordTest: unmasked range 3-4 #6");
|
|
|
|
|
|
const kEmoji = String.fromCodePoint(0x1f914);
|
|
password.value = `${kEmoji}${kEmoji}${kEmoji}`
|
|
|
|
editor.mask();
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range is not specified");
|
|
|
|
editor.unmask(0, 2);
|
|
checkRange(0, 6, `${kEmoji}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 0-2 #1");
|
|
checkRange(0, 2, `${kEmoji}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 0-2 #2");
|
|
checkRange(2, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 0-2 #3");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 0-2 #4");
|
|
|
|
editor.unmask(2, 4);
|
|
checkRange(0, 6, `${kMask}${kMask}${kEmoji}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 2-4 #1");
|
|
checkRange(0, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 2-4 #2");
|
|
checkRange(2, 2, `${kEmoji}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 2-4 #3");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 2-4 #4");
|
|
|
|
editor.unmask(4, 6);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kEmoji}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 4-6 #1");
|
|
checkRange(0, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 4-6 #2");
|
|
checkRange(2, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 4-6 #3");
|
|
checkRange(4, 2, `${kEmoji}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 4-6 #4");
|
|
|
|
editor.unmask(0, 1);
|
|
checkRange(0, 6, `${kEmoji}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 0-1");
|
|
|
|
editor.unmask(1, 2);
|
|
checkRange(0, 6, `${kEmoji}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 1-2");
|
|
|
|
editor.unmask(2, 3);
|
|
checkRange(0, 6, `${kMask}${kMask}${kEmoji}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 2-3");
|
|
|
|
editor.unmask(3, 4);
|
|
checkRange(0, 6, `${kMask}${kMask}${kEmoji}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 3-4");
|
|
|
|
editor.unmask(4, 5);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kEmoji}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 4-5");
|
|
|
|
editor.unmask(5, 6);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kEmoji}`,
|
|
"runQueryPasswordTest: Emojis in password, unmasked range 5-6");
|
|
|
|
|
|
const kEmojiSuperhero = String.fromCodePoint(0x1f9b8);
|
|
const kEmojiMediumSkinTone = String.fromCodePoint(0x1f3fd);
|
|
const kZeroWidthJoiner = "\u200d";
|
|
const kFemaleSign = "\u2640";
|
|
const kVariationSelector16 = "\ufe0f";
|
|
const kComplicatedEmoji = `${kEmojiSuperhero}${kEmojiMediumSkinTone}${kZeroWidthJoiner}${kFemaleSign}${kVariationSelector16}`;
|
|
password.value = `${kComplicatedEmoji}${kComplicatedEmoji}${kComplicatedEmoji}`
|
|
editor.mask();
|
|
checkRange(0, 21, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range is not specified");
|
|
|
|
editor.unmask(0, 7);
|
|
checkRange(0, 21, `${kComplicatedEmoji}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 0-7 #1");
|
|
checkRange(0, 7, `${kComplicatedEmoji}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 0-7 #2");
|
|
checkRange(7, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 0-7 #3");
|
|
checkRange(14, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 0-7 #4");
|
|
|
|
editor.unmask(7, 14);
|
|
checkRange(0, 21, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kComplicatedEmoji}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 7-14 #1");
|
|
checkRange(0, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 7-14 #2");
|
|
checkRange(7, 7, `${kComplicatedEmoji}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 7-14 #3");
|
|
checkRange(14, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 7-14 #4");
|
|
|
|
editor.unmask(14, 21);
|
|
checkRange(0, 21, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kComplicatedEmoji}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 14-21 #1");
|
|
checkRange(0, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 14-21 #2");
|
|
checkRange(7, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 14-21 #3");
|
|
checkRange(14, 7, `${kComplicatedEmoji}`,
|
|
"runQueryPasswordTest: Complicated emojis in password, unmasked range 14-21 #4");
|
|
|
|
password.value = `${kComplicatedEmoji}`
|
|
editor.unmask(0, 1);
|
|
checkRange(0, 7, `${kEmojiSuperhero}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 0-1");
|
|
|
|
editor.unmask(1, 2);
|
|
checkRange(0, 7, `${kEmojiSuperhero}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 1-2");
|
|
|
|
editor.unmask(2, 3);
|
|
checkRange(0, 7, `${kMask}${kMask}${kEmojiMediumSkinTone}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 2-3");
|
|
|
|
editor.unmask(3, 4);
|
|
checkRange(0, 7, `${kMask}${kMask}${kEmojiMediumSkinTone}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 3-4");
|
|
|
|
editor.unmask(4, 5);
|
|
checkRange(0, 7, `${kMask}${kMask}${kMask}${kMask}${kZeroWidthJoiner}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 4-5");
|
|
|
|
editor.unmask(5, 6);
|
|
checkRange(0, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kFemaleSign}${kMask}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 5-6");
|
|
|
|
editor.unmask(6, 7);
|
|
checkRange(0, 7, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kVariationSelector16}`,
|
|
"runQueryPasswordTest: Complicated emoji in password, unmasked range 6-7");
|
|
|
|
|
|
const kKanji = "\u8fba";
|
|
const kIVS = String.fromCodePoint(0xe0101);
|
|
const kKanjiWithIVS = `${kKanji}${kIVS}`;
|
|
password.value = `${kKanjiWithIVS}${kKanjiWithIVS}${kKanjiWithIVS}`
|
|
|
|
editor.mask();
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range is not specified");
|
|
|
|
editor.unmask(0, 3);
|
|
checkRange(0, 9, `${kKanjiWithIVS}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #1");
|
|
checkRange(0, 3, `${kKanjiWithIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #2");
|
|
checkRange(1, 3, `${kIVS}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #3");
|
|
checkRange(0, 1, `${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #4");
|
|
checkRange(1, 2, `${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #5");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #6");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #7");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #8");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #9");
|
|
|
|
editor.unmask(0, 1);
|
|
checkRange(0, 9, `${kKanji}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #1");
|
|
checkRange(0, 1, `${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #2");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #3");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #4");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #5");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #6");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-1 #7");
|
|
|
|
editor.unmask(1, 3);
|
|
checkRange(0, 9, `${kMask}${kIVS}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #1");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #2");
|
|
checkRange(1, 2, `${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #3");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #4");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #5");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #6");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-3 #7");
|
|
|
|
editor.unmask(3, 6);
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kKanjiWithIVS}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #1");
|
|
checkRange(3, 3, `${kKanjiWithIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #2");
|
|
checkRange(4, 3, `${kIVS}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #3");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #4");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #5");
|
|
checkRange(3, 1, `${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #6");
|
|
checkRange(4, 2, `${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #7");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #8");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-6 #9");
|
|
|
|
editor.unmask(3, 4);
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kKanji}${kMask}${kMask}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #1");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #2");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #3");
|
|
checkRange(3, 1, `${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #4");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #5");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #6");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-4 #7");
|
|
|
|
editor.unmask(4, 6);
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kMask}${kIVS}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #1");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #2");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #3");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #4");
|
|
checkRange(4, 2, `${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #5");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #6");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-6 #7");
|
|
|
|
editor.unmask(6, 9);
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kKanjiWithIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #1");
|
|
checkRange(6, 3, `${kKanjiWithIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #2");
|
|
checkRange(4, 3, `${kMask}${kMask}${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #3");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #4");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #5");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #6");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #7");
|
|
checkRange(6, 1, `${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #8");
|
|
checkRange(7, 2, `${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-9 #9");
|
|
|
|
editor.unmask(6, 7);
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kKanji}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #1");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #2");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #3");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #4");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #5");
|
|
checkRange(6, 1, `${kKanji}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #6");
|
|
checkRange(7, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 6-7 #7");
|
|
|
|
editor.unmask(7, 9);
|
|
checkRange(0, 9, `${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kMask}${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #1");
|
|
checkRange(0, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #2");
|
|
checkRange(1, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #3");
|
|
checkRange(3, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #4");
|
|
checkRange(4, 2, `${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #5");
|
|
checkRange(6, 1, `${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #6");
|
|
checkRange(7, 2, `${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 7-9 #7");
|
|
|
|
password.value = `${kKanjiWithIVS}${kKanjiWithIVS}`;
|
|
editor.unmask(0, 2);
|
|
checkRange(0, 6, `${kKanjiWithIVS}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 0-2");
|
|
|
|
editor.unmask(1, 2);
|
|
checkRange(0, 6, `${kMask}${kIVS}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 1-2");
|
|
|
|
editor.unmask(2, 3);
|
|
checkRange(0, 6, `${kMask}${kIVS}${kMask}${kMask}${kMask}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 2-3");
|
|
|
|
editor.unmask(3, 5);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kKanjiWithIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 3-5");
|
|
|
|
editor.unmask(4, 5);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 4-5");
|
|
|
|
editor.unmask(5, 6);
|
|
checkRange(0, 6, `${kMask}${kMask}${kMask}${kMask}${kIVS}`,
|
|
"runQueryPasswordTest: Pairs of Kanji and IVS in password, unmasked range 5-6");
|
|
|
|
editor.mask();
|
|
}
|
|
|
|
function runQueryContentEventRelativeToInsertionPoint()
|
|
{
|
|
textarea.focus();
|
|
textarea.value = "0123456789";
|
|
|
|
// "[]0123456789"
|
|
let startOffset = textarea.selectionStart = textarea.selectionEnd = 0;
|
|
if (!checkContentRelativeToSelection(0, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#1") ||
|
|
!checkContentRelativeToSelection(-1, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#2") ||
|
|
!checkContentRelativeToSelection(1, 1, 1, "1", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#3") ||
|
|
!checkContentRelativeToSelection(5, 10, 5, "56789", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#4") ||
|
|
!checkContentRelativeToSelection(10, 1, 10, "", "runQueryContentEventRelativeToInsertionPoint[0-0]", "#5")) {
|
|
return;
|
|
}
|
|
|
|
// "[01234]56789"
|
|
textarea.selectionEnd = 5;
|
|
if (!checkContentRelativeToSelection(0, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#1") ||
|
|
!checkContentRelativeToSelection(-1, 1, 0, "0", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#2") ||
|
|
!checkContentRelativeToSelection(1, 1, 1, "1", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#3") ||
|
|
!checkContentRelativeToSelection(5, 10, 5, "56789", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#4") ||
|
|
!checkContentRelativeToSelection(10, 1, 10, "", "runQueryContentEventRelativeToInsertionPoint[0-5]", "#5")) {
|
|
return;
|
|
}
|
|
|
|
// "0123[]456789"
|
|
startOffset = textarea.selectionStart = textarea.selectionEnd = 4;
|
|
if (!checkContentRelativeToSelection(0, 1, startOffset + 0, "4", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#1") ||
|
|
!checkContentRelativeToSelection(-1, 1, startOffset - 1, "3", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#2") ||
|
|
!checkContentRelativeToSelection(1, 1, startOffset + 1, "5", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#3") ||
|
|
!checkContentRelativeToSelection(5, 10, startOffset + 5, "9", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#4") ||
|
|
!checkContentRelativeToSelection(10, 1, 10, "", "runQueryContentEventRelativeToInsertionPoint[4-4]", "#5")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "a",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
// "0123[a]456789"
|
|
if (!checkContentRelativeToSelection(0, 1, startOffset + 0, "a", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#1") ||
|
|
!checkContentRelativeToSelection(-1, 1, startOffset - 1, "3", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#2") ||
|
|
!checkContentRelativeToSelection(1, 1, startOffset + 1, "4", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#3") ||
|
|
!checkContentRelativeToSelection(5, 10, startOffset + 5, "89", "runQueryContentEventRelativeToInsertionPoint[composition at 4]", "#4") ||
|
|
!checkContentRelativeToSelection(11, 1, 11, "", "runQueryContentEventRelativeToInsertionPoint[composition at 4]")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
// Move start of composition at first compositionupdate event.
|
|
function onCompositionUpdate()
|
|
{
|
|
startOffset = textarea.selectionStart = textarea.selectionEnd = textarea.selectionStart - 1;
|
|
textarea.removeEventListener("compositionupdate", onCompositionUpdate);
|
|
}
|
|
textarea.addEventListener("compositionupdate", onCompositionUpdate);
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "b",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
// "0123[b]a456789"
|
|
if (!checkContentRelativeToSelection(0, 1, startOffset + 0, "b", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#1") ||
|
|
!checkContentRelativeToSelection(-1, 1, startOffset - 1, "3", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#2") ||
|
|
!checkContentRelativeToSelection(1, 1, startOffset + 1, "a", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#3") ||
|
|
!checkContentRelativeToSelection(5, 10, startOffset + 5, "789", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#4") ||
|
|
!checkContentRelativeToSelection(12, 1, 12, "", "runQueryContentEventRelativeToInsertionPoint[composition at 3]", "#5")) {
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
return;
|
|
}
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
}
|
|
|
|
function runBug1375825Test()
|
|
{
|
|
contenteditable.focus();
|
|
|
|
// #1
|
|
contenteditable.innerHTML = "abc<span contenteditable=\"false\">defgh</span>";
|
|
|
|
let ret = synthesizeQueryTextRect(2, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #1 (2, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "c", "runBug1375825Test #1 (2, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'c'");
|
|
|
|
ret = synthesizeQueryTextRect(3, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #1 (3, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "d", "runBug1375825Test #1 (3, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'd'");
|
|
|
|
ret = synthesizeQueryTextRect(4, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #1 (4, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "e", "runBug1375825Test #1 (4, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'e'");
|
|
|
|
ret = synthesizeQueryTextRect(5, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #1 (5, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "f", "runBug1375825Test #1 (5, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'f'");
|
|
|
|
ret = synthesizeQueryTextRect(6, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #1 (6, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "g", "runBug1375825Test #1 (6, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'g'");
|
|
|
|
ret = synthesizeQueryTextRect(7, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #1 (7, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "h", "runBug1375825Test #1 (7, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'h'");
|
|
|
|
// #2
|
|
contenteditable.innerHTML = "abc<span style=\"user-select: all;\">defgh</span>";
|
|
|
|
ret = synthesizeQueryTextRect(2, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #2 (2, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "c", "runBug1375825Test #2 (2, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'c'");
|
|
|
|
ret = synthesizeQueryTextRect(3, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #2 (3, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "d", "runBug1375825Test #2 (3, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'd'");
|
|
|
|
ret = synthesizeQueryTextRect(4, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #2 (4, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "e", "runBug1375825Test #2 (4, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'e'");
|
|
|
|
ret = synthesizeQueryTextRect(5, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #2 (5, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "f", "runBug1375825Test #2 (5, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'f'");
|
|
|
|
ret = synthesizeQueryTextRect(6, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #2 (6, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "g", "runBug1375825Test #2 (6, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'g'");
|
|
|
|
ret = synthesizeQueryTextRect(7, 1);
|
|
if (!checkQueryContentResult(ret, "runBug1375825Test #2 (7, 1), \"" + contenteditable.innerHTML + "\"")) {
|
|
return;
|
|
}
|
|
is(ret.text, "h", "runBug1375825Test #2 (7, 1), \"" + contenteditable.innerHTML + "\": should have queried a rect for 'h'");
|
|
}
|
|
|
|
function runBug1530649Test()
|
|
{
|
|
// Vietnamese IME on macOS commits composition with typing space key.
|
|
// Then, typing new word shouldn't trim the trailing whitespace.
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "";
|
|
synthesizeCompositionChange(
|
|
{composition: {string: "abc", clauses: [{length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
caret: {start: 3, length: 0}});
|
|
synthesizeComposition({type: "compositioncommit", data: "abc ", key: " "});
|
|
|
|
is(contenteditable.innerHTML, "abc <br>",
|
|
"runBug1530649Test: The trailing space shouldn't be removed");
|
|
|
|
synthesizeCompositionChange(
|
|
{composition: {string: "d", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
caret: {start: 1, length: 0}});
|
|
|
|
is(contenteditable.innerHTML, "abc d",
|
|
"runBug1530649Test: The new composition string shouldn't remove the last space");
|
|
|
|
synthesizeComposition({type: "compositioncommitasis", key: "KEY_Enter"});
|
|
|
|
is(contenteditable.innerHTML, "abc d",
|
|
"runBug1530649Test: Committing the new composition string shouldn't remove the last space");
|
|
}
|
|
|
|
function runBug1571375Test()
|
|
{
|
|
let selection = windowOfContenteditableBySpan.getSelection();
|
|
let doc = document.getElementById("iframe7").contentDocument;
|
|
|
|
contenteditableBySpan.focus();
|
|
|
|
contenteditableBySpan.innerHTML = "hello world";
|
|
let range = doc.createRange();
|
|
range.setStart(contenteditableBySpan.firstChild, 6);
|
|
range.setEnd(contenteditableBySpan.firstChild, 11);
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "world", clauses: [{length: 5, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
caret: { start: 5, length: 0 },
|
|
});
|
|
synthesizeComposition({type: "compositioncommit", data: "world", key: " "});
|
|
is(contenteditableBySpan.innerHTML, "hello world",
|
|
"runBug1571375Test: space must not be removed by commit");
|
|
|
|
contenteditableBySpan.innerHTML = "hello world";
|
|
range = doc.createRange();
|
|
range.setStart(contenteditableBySpan.firstChild, 0);
|
|
range.setEnd(contenteditableBySpan.firstChild, 5);
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "hello", clauses: [{length: 5, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
caret: { start: 5, length: 0 },
|
|
});
|
|
synthesizeComposition({type: "compositioncommit", data: "hello", key: " "});
|
|
is(contenteditableBySpan.innerHTML, "hello world",
|
|
"runBug1571375Test: space must not be removed by commit");
|
|
|
|
contenteditableBySpan.innerHTML = "hello world<div>.</div>";
|
|
range = doc.createRange();
|
|
range.setStart(contenteditableBySpan.firstChild, 6);
|
|
range.setEnd(contenteditableBySpan.firstChild, 11);
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "world", clauses: [{length: 5, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
caret: {start: 0, length: 0}}
|
|
);
|
|
synthesizeComposition({type: "compositioncommit", data: "world", key: " "});
|
|
is(contenteditableBySpan.innerHTML, "hello world<div>.</div>",
|
|
"runBug1571375Test: space must not be removed by commit");
|
|
}
|
|
|
|
async function runBug1584901Test()
|
|
{
|
|
contenteditableBySpan.focus();
|
|
contenteditableBySpan.innerHTML = "";
|
|
|
|
// XXX synthesizeCompositionChange won't work without wait.
|
|
await waitForTick();
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a ", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeComposition({type: "compositioncommitasis", key: " "});
|
|
|
|
is(contenteditableBySpan.innerHTML, "a ",
|
|
"runBug1584901Test: space must not be removed by composition change");
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "b ", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeComposition({type: "compositioncommitasis", key: " "});
|
|
|
|
is(contenteditableBySpan.innerHTML, "a b ",
|
|
"runBug1584901Test: space must not be removed by composition change");
|
|
}
|
|
|
|
function runBug1675313Test()
|
|
{
|
|
input.value = "";
|
|
input.focus();
|
|
let count = 0;
|
|
|
|
function handler() {
|
|
input.focus();
|
|
count++;
|
|
}
|
|
|
|
input.addEventListener("keydown", handler);
|
|
input.addEventListener("keyup", handler);
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {
|
|
string: "a",
|
|
clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
|
|
key: { key: "a", type: "keyup" },
|
|
},
|
|
});
|
|
synthesizeCompositionChange({
|
|
composition: {
|
|
string: "b",
|
|
clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
|
|
key: { key: "b", type: "keyup" },
|
|
},
|
|
});
|
|
synthesizeComposition({type: "compositioncommitasis"});
|
|
|
|
is(count, 6, "runBug1675313Test: keydown event and keyup event are fired correctly");
|
|
is(input.value, "b",
|
|
"runBug1675313Test: re-focus element doesn't commit composition if re-focus isn't click by user");
|
|
|
|
input.removeEventListener("keyup", handler);
|
|
}
|
|
|
|
function runCommitCompositionWithSpaceKey()
|
|
{
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "";
|
|
|
|
// Last white space might be if last child is no <br>
|
|
// Actually, our implementation will insert <br> element at last child, so
|
|
// white space will be ASCII space.
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeComposition({type: "compositioncommit", data: "a"});
|
|
synthesizeKey(" ");
|
|
|
|
is(contenteditable.innerHTML, "a <br>",
|
|
"runCommitCompositionWithSpaceKey: last single space should be kept");
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "b", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeComposition({type: "compositioncommit", data: "b"});
|
|
synthesizeKey(" ");
|
|
|
|
is(contenteditable.innerHTML, "a b <br>",
|
|
"runCommitCompositionWithSpaceKey: inserting composition shouldn't remove last single space.");
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "c", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeComposition({type: "compositioncommit", data: "c"});
|
|
synthesizeKey(" ");
|
|
|
|
is(contenteditable.innerHTML, "a b c <br>",
|
|
"runCommitCompositionWithSpaceKey: inserting composition shouldn't remove last single space.");
|
|
|
|
contenteditable.innerHTML = "a";
|
|
windowOfContenteditable.getSelection().collapse(contenteditable.firstChild, contenteditable.firstChild.length);
|
|
is(contenteditable.innerHTML, "a",
|
|
"runCommitCompositionWithSpaceKey: contenteditable should be initialized with text ending with a space and without following <br> element");
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "b", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeComposition({type: "compositioncommit", data: "b ", key: { key: " ", code: "Space" }});
|
|
|
|
is(contenteditable.innerHTML, "ab <br>",
|
|
"runCommitCompositionWithSpaceKey: contenteditable should end with a padding <br> element after inserting commit string ending with a space");
|
|
}
|
|
|
|
function runCSSTransformTest()
|
|
{
|
|
textarea.focus();
|
|
textarea.value = "some text";
|
|
textarea.selectionStart = textarea.selectionEnd = textarea.value.length;
|
|
let editorRect = synthesizeQueryEditorRect();
|
|
if (!checkQueryContentResult(editorRect,
|
|
"runCSSTransformTest: editorRect")) {
|
|
return;
|
|
}
|
|
let firstCharRect = synthesizeQueryTextRect(0, 1);
|
|
if (!checkQueryContentResult(firstCharRect,
|
|
"runCSSTransformTest: firstCharRect")) {
|
|
return;
|
|
}
|
|
let lastCharRect = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
|
|
if (!checkQueryContentResult(lastCharRect,
|
|
"runCSSTransformTest: lastCharRect")) {
|
|
return;
|
|
}
|
|
let caretRect = synthesizeQueryCaretRect(textarea.selectionStart);
|
|
if (!checkQueryContentResult(caretRect,
|
|
"runCSSTransformTest: caretRect")) {
|
|
return;
|
|
}
|
|
let caretRectBeforeFirstChar = synthesizeQueryCaretRect(0);
|
|
if (!checkQueryContentResult(caretRectBeforeFirstChar,
|
|
"runCSSTransformTest: caretRectBeforeFirstChar")) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
textarea.style.transform = "translate(10px, 15px)";
|
|
function movedRect(aRect, aCSS_CX, aCSS_CY)
|
|
{
|
|
return {
|
|
left: aRect.left + Math.round(aCSS_CX * window.devicePixelRatio),
|
|
top: aRect.top + Math.round(aCSS_CY * window.devicePixelRatio),
|
|
width: aRect.width,
|
|
height: aRect.height
|
|
};
|
|
}
|
|
|
|
let editorRectTranslated = synthesizeQueryEditorRect();
|
|
if (!checkQueryContentResult(editorRectTranslated,
|
|
"runCSSTransformTest: editorRectTranslated, " + textarea.style.transform) ||
|
|
!checkRectFuzzy(editorRectTranslated, movedRect(editorRect, 10, 15), {left: 1, top: 1, width: 1, height: 1},
|
|
"runCSSTransformTest: editorRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
let firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
|
|
if (!checkQueryContentResult(firstCharRectTranslated,
|
|
"runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform) ||
|
|
!checkRectFuzzy(firstCharRectTranslated, movedRect(firstCharRect, 10, 15), {left: 1, top: 1, width: 1, height: 1},
|
|
"runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
let lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
|
|
if (!checkQueryContentResult(lastCharRectTranslated,
|
|
"runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform) ||
|
|
!checkRectFuzzy(lastCharRectTranslated, movedRect(lastCharRect, 10, 15), {left: 1, top: 1, width: 1, height: 1},
|
|
"runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
let caretRectTranslated = synthesizeQueryCaretRect(textarea.selectionStart);
|
|
if (!checkQueryContentResult(caretRectTranslated,
|
|
"runCSSTransformTest: caretRectTranslated, " + textarea.style.transform) ||
|
|
!checkRectFuzzy(caretRectTranslated, movedRect(caretRect, 10, 15), {left: 1, top: 1, width: 1, height: 1},
|
|
"runCSSTransformTest: caretRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
let caretRectBeforeFirstCharTranslated = synthesizeQueryCaretRect(0);
|
|
if (!checkQueryContentResult(caretRectBeforeFirstCharTranslated,
|
|
"runCSSTransformTest: caretRectBeforeFirstCharTranslated, " + textarea.style.transform) ||
|
|
!checkRectFuzzy(caretRectBeforeFirstCharTranslated, movedRect(caretRectBeforeFirstChar, 10, 15), {left: 1, top: 1, width: 1, height: 1},
|
|
"runCSSTransformTest: caretRectBeforeFirstCharTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
let firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
|
|
if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
|
|
!checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
let lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
|
|
if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
|
|
!checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
|
|
// XXX It's too difficult to check the result with scale and rotate...
|
|
// For now, let's check if query text rect and query text rect array returns same rect.
|
|
textarea.style.transform = "scale(1.5)";
|
|
firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
|
|
if (!checkQueryContentResult(firstCharRectTranslated,
|
|
"runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
|
|
if (!checkQueryContentResult(lastCharRectTranslated,
|
|
"runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
|
|
if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
|
|
!checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
|
|
if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
|
|
!checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
|
|
textarea.style.transform = "rotate(30deg)";
|
|
firstCharRectTranslated = synthesizeQueryTextRect(0, 1);
|
|
if (!checkQueryContentResult(firstCharRectTranslated,
|
|
"runCSSTransformTest: firstCharRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
lastCharRectTranslated = synthesizeQueryTextRect(textarea.value.length - 1, textarea.value.length);
|
|
if (!checkQueryContentResult(lastCharRectTranslated,
|
|
"runCSSTransformTest: lastCharRectTranslated, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
firstCharRectTranslatedAsArray = synthesizeQueryTextRectArray(0, 1);
|
|
if (!checkQueryContentResult(firstCharRectTranslatedAsArray, "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform) ||
|
|
!checkRectArray(firstCharRectTranslatedAsArray, [firstCharRectTranslated], "runCSSTransformTest: firstCharRectTranslatedAsArray, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
lastCharRectTranslatedAsArray = synthesizeQueryTextRectArray(textarea.value.length - 1, textarea.value.length);
|
|
checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform);
|
|
checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform);
|
|
} finally {
|
|
textarea.style.transform = "";
|
|
}
|
|
}
|
|
|
|
function runBug722639Test()
|
|
{
|
|
textarea.focus();
|
|
textarea.value = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
|
|
textarea.value += textarea.value;
|
|
textarea.value += textarea.value; // 80 characters
|
|
|
|
let firstLine = synthesizeQueryTextRect(0, 1);
|
|
if (!checkQueryContentResult(firstLine,
|
|
"runBug722639Test: firstLine")) {
|
|
return;
|
|
}
|
|
ok(true, "runBug722639Test: 1st line, top=" + firstLine.top + ", left=" + firstLine.left);
|
|
let firstLineAsArray = synthesizeQueryTextRectArray(0, 1);
|
|
if (!checkQueryContentResult(firstLineAsArray, "runBug722639Test: 1st line as array") ||
|
|
!checkRectArray(firstLineAsArray, [firstLine], "runBug722639Test: 1st line as array should match with text rect result")) {
|
|
return;
|
|
}
|
|
if (kLFLen > 1) {
|
|
let firstLineLF = synthesizeQueryTextRect(1, 1);
|
|
if (!checkQueryContentResult(firstLineLF,
|
|
"runBug722639Test: firstLineLF")) {
|
|
return;
|
|
}
|
|
is(firstLineLF.top, firstLine.top, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
|
|
is(firstLineLF.left, firstLine.left, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
|
|
isfuzzy(firstLineLF.height, firstLine.height, 1,
|
|
"runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
|
|
is(firstLineLF.width, firstLine.width, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
|
|
let firstLineLFAsArray = synthesizeQueryTextRectArray(1, 1);
|
|
if (!checkQueryContentResult(firstLineLFAsArray, "runBug722639Test: 1st line's \\n rect as array") ||
|
|
!checkRectArray(firstLineLFAsArray, [firstLineLF], "runBug722639Test: 1st line's rect as array should match with text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
let secondLine = synthesizeQueryTextRect(kLFLen, 1);
|
|
if (!checkQueryContentResult(secondLine,
|
|
"runBug722639Test: secondLine")) {
|
|
return;
|
|
}
|
|
ok(true, "runBug722639Test: 2nd line, top=" + secondLine.top + ", left=" + secondLine.left);
|
|
let secondLineAsArray = synthesizeQueryTextRectArray(kLFLen, 1);
|
|
if (!checkQueryContentResult(secondLineAsArray, "runBug722639Test: 2nd line as array") ||
|
|
!checkRectArray(secondLineAsArray, [secondLine], "runBug722639Test: 2nd line as array should match with text rect result")) {
|
|
return;
|
|
}
|
|
if (kLFLen > 1) {
|
|
let secondLineLF = synthesizeQueryTextRect(kLFLen + 1, 1);
|
|
if (!checkQueryContentResult(secondLineLF,
|
|
"runBug722639Test: secondLineLF")) {
|
|
return;
|
|
}
|
|
is(secondLineLF.top, secondLine.top, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
|
|
is(secondLineLF.left, secondLine.left, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
|
|
isfuzzy(secondLineLF.height, secondLine.height, 1,
|
|
"runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
|
|
is(secondLineLF.width, secondLine.width, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
|
|
let secondLineLFAsArray = synthesizeQueryTextRectArray(kLFLen + 1, 1);
|
|
if (!checkQueryContentResult(secondLineLFAsArray, "runBug722639Test: 2nd line's \\n rect as array") ||
|
|
!checkRectArray(secondLineLFAsArray, [secondLineLF], "runBug722639Test: 2nd line's rect as array should match with text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
let lineHeight = secondLine.top - firstLine.top;
|
|
ok(lineHeight > 0,
|
|
"runBug722639Test: lineHeight must be positive");
|
|
is(secondLine.left, firstLine.left,
|
|
"runBug722639Test: the left value must be always same value");
|
|
isfuzzy(secondLine.height, firstLine.height, 1,
|
|
"runBug722639Test: the height must be always same value");
|
|
let previousTop = secondLine.top;
|
|
for (let i = 3; i <= textarea.value.length + 1; i++) {
|
|
let currentLine = synthesizeQueryTextRect(kLFLen * (i - 1), 1);
|
|
if (!checkQueryContentResult(currentLine,
|
|
"runBug722639Test: " + i + "th currentLine")) {
|
|
return;
|
|
}
|
|
ok(true, "runBug722639Test: " + i + "th line, top=" + currentLine.top + ", left=" + currentLine.left);
|
|
let currentLineAsArray = synthesizeQueryTextRectArray(kLFLen * (i - 1), 1);
|
|
if (!checkQueryContentResult(currentLineAsArray, "runBug722639Test: " + i + "th line as array") ||
|
|
!checkRectArray(currentLineAsArray, [currentLine], "runBug722639Test: " + i + "th line as array should match with text rect result")) {
|
|
return;
|
|
}
|
|
// NOTE: the top position may be 1px larger or smaller than other lines
|
|
// due to sub pixel positioning.
|
|
if (Math.abs(currentLine.top - (previousTop + lineHeight)) <= 1) {
|
|
ok(true, "runBug722639Test: " + i + "th line's top is expected");
|
|
} else {
|
|
is(currentLine.top, previousTop + lineHeight,
|
|
"runBug722639Test: " + i + "th line's top is unexpected");
|
|
}
|
|
is(currentLine.left, firstLine.left,
|
|
"runBug722639Test: " + i + "th line's left is unexpected");
|
|
isfuzzy(currentLine.height, firstLine.height, 1,
|
|
`runBug722639Test: ${i}th line's height is unexpected`);
|
|
if (kLFLen > 1) {
|
|
let currentLineLF = synthesizeQueryTextRect(kLFLen * (i - 1) + 1, 1);
|
|
if (!checkQueryContentResult(currentLineLF,
|
|
"runBug722639Test: " + i + "th currentLineLF")) {
|
|
return;
|
|
}
|
|
is(currentLineLF.top, currentLine.top, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
|
|
is(currentLineLF.left, currentLine.left, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
|
|
isfuzzy(currentLineLF.height, currentLine.height, 1,
|
|
`runBug722639Test: ${i}th line's \\n rect should be same as same line's \\r rect`);
|
|
is(currentLineLF.width, currentLine.width, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
|
|
let currentLineLFAsArray = synthesizeQueryTextRectArray(kLFLen * (i - 1) + 1, 1);
|
|
if (!checkQueryContentResult(currentLineLFAsArray, "runBug722639Test: " + i + "th line's \\n rect as array") ||
|
|
!checkRectArray(currentLineLFAsArray, [currentLineLF], "runBug722639Test: " + i + "th line's rect as array should match with text rect result")) {
|
|
return;
|
|
}
|
|
}
|
|
previousTop = currentLine.top;
|
|
}
|
|
}
|
|
|
|
function runCompositionWithSelectionChange() {
|
|
function doTest(aEditor, aDescription) {
|
|
aEditor.focus();
|
|
const isHTMLEditor =
|
|
aEditor.nodeName.toLowerCase() != "input" && aEditor.nodeName.toLowerCase() != "textarea";
|
|
const win = isHTMLEditor ? windowOfContenteditable : window;
|
|
function getValue() {
|
|
return isHTMLEditor ? aEditor.innerHTML : aEditor.value;
|
|
}
|
|
function setSelection(aStart, aLength) {
|
|
if (isHTMLEditor) {
|
|
win.getSelection().setBaseAndExtent(aEditor.firstChild, aStart, aEditor.firstChild, aStart + aLength);
|
|
} else {
|
|
aEditor.setSelectionRange(aStart, aStart + aLength);
|
|
}
|
|
}
|
|
|
|
if (isHTMLEditor) {
|
|
aEditor.innerHTML = "abcxyz";
|
|
} else {
|
|
aEditor.value = "abcxyz";
|
|
}
|
|
setSelection("abc".length, 0);
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {
|
|
string: "1",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
|
|
caret: { start: 1, length: 0 },
|
|
}
|
|
});
|
|
|
|
is(getValue(), "abc1xyz",
|
|
`${aDescription}: First composing character should be inserted middle of the text`);
|
|
|
|
aEditor.addEventListener("compositionupdate", () => {
|
|
setSelection("abc".length, "1".length);
|
|
}, {once: true});
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {
|
|
string: "12",
|
|
clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
|
|
caret: { start: 2, length: 0 },
|
|
}
|
|
});
|
|
|
|
is(getValue(), "abc12xyz",
|
|
`${aDescription}: Only composition string should be updated even if selection range is updated by "compositionupdate" event listener`);
|
|
|
|
aEditor.addEventListener("compositionupdate", () => {
|
|
setSelection("abc1".length, "2d".length);
|
|
}, {once: true});
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {
|
|
string: "123",
|
|
clauses: [{ length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
|
|
caret: { start: 3, length: 0 },
|
|
}
|
|
});
|
|
|
|
is(getValue(), "abc123xyz",
|
|
`${aDescription}: Only composition string should be updated even if selection range wider than composition string is updated by "compositionupdate" event listener`);
|
|
|
|
aEditor.addEventListener("compositionupdate", () => {
|
|
setSelection("ab".length, "c123d".length);
|
|
}, {once: true});
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {
|
|
string: "456",
|
|
clauses: [{ length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
|
|
caret: { start: 3, length: 0 },
|
|
}
|
|
});
|
|
|
|
is(getValue(), "abc456xyz",
|
|
`${aDescription}: Only composition string should be updated even if selection range which covers all over the composition string is updated by "compositionupdate" event listener`);
|
|
|
|
aEditor.addEventListener("beforeinput", () => {
|
|
setSelection("abc456d".length, 0);
|
|
}, {once: true});
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
is(getValue(), "abc456xyz",
|
|
`${aDescription}: Only composition string should be updated when committing composition but selection is updated by "beforeinput" event listener`);
|
|
if (isHTMLEditor) {
|
|
is(win.getSelection().focusNode, aEditor.firstChild,
|
|
`${aDescription}: The focus node after composition should be the text node`);
|
|
is(win.getSelection().focusOffset, "abc456".length,
|
|
`${aDescription}: The focus offset after composition should be end of the composition string`);
|
|
is(win.getSelection().anchorNode, aEditor.firstChild,
|
|
`${aDescription}: The anchor node after composition should be the text node`);
|
|
is(win.getSelection().anchorOffset, "abc456".length,
|
|
`${aDescription}: The anchor offset after composition should be end of the composition string`);
|
|
} else {
|
|
is(aEditor.selectionStart, "abc456".length,
|
|
`${aDescription}: The selectionStart after composition should be end of the composition string`);
|
|
is(aEditor.selectionEnd, "abc456".length,
|
|
`${aDescription}: The selectionEnd after composition should be end of the composition string`);
|
|
}
|
|
}
|
|
doTest(textarea, "runCompositionWithSelectionChange(textarea)");
|
|
doTest(input, "runCompositionWithSelectionChange(input)");
|
|
doTest(contenteditable, "runCompositionWithSelectionChange(contenteditable)");
|
|
}
|
|
|
|
function runCompositionWithClick() {
|
|
let events = [];
|
|
function eventHandler(aEvent) {
|
|
events.push(aEvent);
|
|
}
|
|
function stringifyEventAt(aIndex) {
|
|
if (aIndex >= events.length) {
|
|
return "<undefined>";
|
|
}
|
|
return `${events[aIndex].type}@${events[aIndex].target?.nodeName}`;
|
|
}
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
input.value = "";
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "abc",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
for (const type of ["compositionend", "input", "mousedown", "blur"]) {
|
|
window.addEventListener(type, eventHandler, {capture: true});
|
|
}
|
|
// Commit the composition with clicking outside the composition string (in
|
|
// this case, another text control).
|
|
synthesizeMouseAtCenter(input, {});
|
|
for (const type of ["compositionend", "input", "mousedown", "blur"]) {
|
|
window.removeEventListener(type, eventHandler, {capture: true});
|
|
}
|
|
|
|
let index = -1;
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(
|
|
stringifyEventAt(++index),
|
|
"input@textarea",
|
|
`runCompositionWithClick: events[${index}]`
|
|
);
|
|
}
|
|
is(
|
|
stringifyEventAt(++index),
|
|
"compositionend@textarea",
|
|
`runCompositionWithClick: events[${index}]`
|
|
);
|
|
is(
|
|
stringifyEventAt(++index),
|
|
"input@textarea",
|
|
`runCompositionWithClick: events[${index}]`
|
|
);
|
|
is(
|
|
stringifyEventAt(++index),
|
|
"mousedown@input",
|
|
`runCompositionWithClick: events[${index}]`
|
|
);
|
|
is(
|
|
stringifyEventAt(++index),
|
|
"blur@textarea",
|
|
`runCompositionWithClick: events[${index}]`
|
|
);
|
|
is(textarea.value, "abc", "runCompositionWithClick: Composition should be committed in <textarea> which had it");
|
|
is(input.value, "", "runCompositionWithClick: Composition should not be committed in <input> which newly gets focus");
|
|
}
|
|
|
|
function runForceCommitTest()
|
|
{
|
|
let events;
|
|
function eventHandler(aEvent)
|
|
{
|
|
events.push(aEvent);
|
|
}
|
|
window.addEventListener("compositionstart", eventHandler, true);
|
|
window.addEventListener("compositionupdate", eventHandler, true);
|
|
window.addEventListener("compositionend", eventHandler, true);
|
|
window.addEventListener("beforeinput", eventHandler, true);
|
|
window.addEventListener("input", eventHandler, true);
|
|
window.addEventListener("text", eventHandler, true);
|
|
|
|
// Make the composition in textarea commit by click in the textarea
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
events = [];
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
is(events.length, 5,
|
|
"runForceCommitTest: wrong event count #1");
|
|
is(events[0].type, "compositionstart",
|
|
"runForceCommitTest: the 1st event must be compositionstart #1");
|
|
is(events[1].type, "compositionupdate",
|
|
"runForceCommitTest: the 2nd event must be compositionupdate #1");
|
|
is(events[2].type, "text",
|
|
"runForceCommitTest: the 3rd event must be text #1");
|
|
is(events[3].type, "beforeinput",
|
|
"runForceCommitTest: the 4th event must be beforeinput #1");
|
|
checkInputEvent(events[3], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #1");
|
|
is(events[4].type, "input",
|
|
"runForceCommitTest: the 5th event must be input #1");
|
|
checkInputEvent(events[4], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #1");
|
|
|
|
(() => {
|
|
events = [];
|
|
synthesizeMouseAtCenter(textarea, {});
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #2");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #2`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #2`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #2");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #2`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #2");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #2`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #2");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #2`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #2");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #2");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #2");
|
|
})();
|
|
|
|
// Make the composition in textarea commit by click in another editor (input)
|
|
(() => {
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
input.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
synthesizeMouseAtCenter(input, {});
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #3");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #3`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #3`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #3`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #3`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #3");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #3`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #3`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #3");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #3`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #3`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #3");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #3`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #3`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #3");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #3");
|
|
ok(!getEditor(input).isComposing,
|
|
"runForceCommitTest: the input has composition #3");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #3");
|
|
is(input.value, "",
|
|
"runForceCommitTest: the input has the committed text? #3");
|
|
})();
|
|
|
|
// Make the composition in textarea commit by blur()
|
|
(() => {
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
textarea.blur();
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #4");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #4`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #4`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #4`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #4");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #4`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #4");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #4`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #4`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #4");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #4`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #4");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #4");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #4");
|
|
})();
|
|
|
|
// Make the composition in textarea commit by input.focus()
|
|
(() => {
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
input.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
input.focus();
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #5");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #5`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #5`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #5`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #5`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #5");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #5`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #5`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #5");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #5`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #5`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #5");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #5`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #5`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #5");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #5");
|
|
ok(!getEditor(input).isComposing,
|
|
"runForceCommitTest: the input has composition #5");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #5");
|
|
is(input.value, "",
|
|
"runForceCommitTest: the input has the committed text? #5");
|
|
})();
|
|
|
|
// Make the composition in textarea commit by click in another document's editor
|
|
(() => {
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
textareaInFrame.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
synthesizeMouseAtCenter(textareaInFrame, {}, iframe.contentWindow);
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #6");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #6`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #6`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #6`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #6`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #6");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #6`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #6`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #6");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #6`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #6`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #6");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #6`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #6`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #6");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #6");
|
|
ok(!getEditor(textareaInFrame).isComposing,
|
|
"runForceCommitTest: the textarea in frame has composition #6");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #6");
|
|
is(textareaInFrame.value, "",
|
|
"runForceCommitTest: the textarea in frame has the committed text? #6");
|
|
})();
|
|
|
|
// Make the composition in textarea commit by another document's editor's focus()
|
|
(() => {
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
textareaInFrame.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
textareaInFrame.focus();
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #7");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #7`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #7`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #7`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #7`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #7");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #7`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #7`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #7");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #7`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #7`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #7");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #7`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #7`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #7");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #7");
|
|
ok(!getEditor(textareaInFrame).isComposing,
|
|
"runForceCommitTest: the textarea in frame has composition #7");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #7");
|
|
is(textareaInFrame.value, "",
|
|
"runForceCommitTest: the textarea in frame has the committed text? #7");
|
|
})();
|
|
|
|
// Make the composition in a textarea commit by click in another editable document
|
|
(() => {
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
iframe2.contentDocument.body.innerHTML = "Text in the Body";
|
|
const iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
synthesizeMouseAtCenter(iframe2.contentDocument.body, {}, iframe2.contentWindow);
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #8");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #8`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #8`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #8`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #8`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #8");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #8`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #8`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #8");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #8`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #8`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #8");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #8`);
|
|
is(events[index].target, textarea,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #8`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #8");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #8");
|
|
ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
|
|
"runForceCommitTest: the editable document has composition #8");
|
|
is(textarea.value, "\u306E",
|
|
"runForceCommitTest: the textarea doesn't have the committed text #8");
|
|
is(iframe2.contentDocument.body.innerHTML, iframe2BodyInnerHTML,
|
|
"runForceCommitTest: the editable document has the committed text? #8");
|
|
})();
|
|
|
|
// Make the composition in an editable document commit by click in it
|
|
(() => {
|
|
iframe2.contentWindow.focus();
|
|
iframe2.contentDocument.body.innerHTML = "Text in the Body";
|
|
const iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
}, iframe2.contentWindow);
|
|
|
|
events = [];
|
|
synthesizeMouseAtCenter(iframe2.contentDocument.body, {}, iframe2.contentWindow);
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #9");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #9`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #9`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #9`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #9`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E",
|
|
[{startContainer: iframe2.contentDocument.body.firstChild,
|
|
startOffset: iframe2.contentDocument.body.firstChild.wholeText.indexOf("\u306E"),
|
|
endContainer: iframe2.contentDocument.body.firstChild,
|
|
endOffset: iframe2.contentDocument.body.firstChild.wholeText.indexOf("\u306E") + 1}],
|
|
"runForceCommitTest #9");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #9`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #9`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #9");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #9`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #9`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #9");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #9`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #9`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #9");
|
|
ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
|
|
"runForceCommitTest: the editable document still has composition #9");
|
|
ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
|
|
iframe2.contentDocument.body.innerHTML.includes("\u306E"),
|
|
"runForceCommitTest: the editable document doesn't have the committed text #9");
|
|
})();
|
|
|
|
// Make the composition in an editable document commit by click in another document's editor
|
|
(() => {
|
|
textarea.value = "";
|
|
iframe2.contentWindow.focus();
|
|
iframe2.contentDocument.body.innerHTML = "Text in the Body";
|
|
const iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
}, iframe2.contentWindow);
|
|
|
|
events = [];
|
|
synthesizeMouseAtCenter(textarea, {});
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #10");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #10`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #10`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #10`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #10`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E",
|
|
[{startContainer: iframe2.contentDocument.body.firstChild,
|
|
startOffset: iframe2.contentDocument.body.firstChild.wholeText.indexOf("\u306E"),
|
|
endContainer: iframe2.contentDocument.body.firstChild,
|
|
endOffset: iframe2.contentDocument.body.firstChild.wholeText.indexOf("\u306E") + 1}],
|
|
"runForceCommitTest #10");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #10`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #10`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #10");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #10`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #10`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #10");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #10`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[index].type} event was fired on wrong event target #10`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #10");
|
|
ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
|
|
"runForceCommitTest: the editable document still has composition #10");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea has composition #10");
|
|
ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
|
|
iframe2.contentDocument.body.innerHTML.includes("\u306E"),
|
|
"runForceCommitTest: the editable document doesn't have the committed text #10");
|
|
is(textarea.value, "",
|
|
"runForceCommitTest: the textarea has the committed text? #10");
|
|
})();
|
|
|
|
// Make the composition in an editable document commit by click in the another editable document
|
|
(() => {
|
|
iframe2.contentWindow.focus();
|
|
iframe2.contentDocument.body.innerHTML = "Text in the Body";
|
|
const iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
|
|
iframe3.contentDocument.body.innerHTML = "Text in the Body";
|
|
const iframe3BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
}, iframe2.contentWindow);
|
|
|
|
events = [];
|
|
synthesizeMouseAtCenter(iframe3.contentDocument.body, {}, iframe3.contentWindow);
|
|
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runForceCommitTest: wrong event count #11");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runForceCommitTest: events[${index}] must be text #11`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #11`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runForceCommitTest: events[${index}] must be beforeinput #11`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #11`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E",
|
|
[{startContainer: iframe2.contentDocument.body.firstChild,
|
|
startOffset: iframe2.contentDocument.body.firstChild.wholeText.indexOf("\u306E"),
|
|
endContainer: iframe2.contentDocument.body.firstChild,
|
|
endOffset: iframe2.contentDocument.body.firstChild.wholeText.indexOf("\u306E") + 1}],
|
|
"runForceCommitTest #11");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #11`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #11`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #11");
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runForceCommitTest: events[${index}] must be compositionend #11`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #11`);
|
|
is(events[index].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #11");
|
|
is(events[++index].type, "input",
|
|
`runForceCommitTest: events[${index}] must be input #11`);
|
|
is(events[index].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[index].type}" event was fired on wrong event target #11`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #11");
|
|
ok(!getHTMLEditorIMESupport(iframe2.contentWindow).isComposing,
|
|
"runForceCommitTest: the editable document still has composition #11");
|
|
ok(!getHTMLEditorIMESupport(iframe3.contentWindow).isComposing,
|
|
"runForceCommitTest: the other editable document has composition #11");
|
|
ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
|
|
iframe2.contentDocument.body.innerHTML.includes("\u306E"),
|
|
"runForceCommitTest: the editable document doesn't have the committed text #11");
|
|
is(iframe3.contentDocument.body.innerHTML, iframe3BodyInnerHTML,
|
|
"runForceCommitTest: the other editable document has the committed text? #11");
|
|
})();
|
|
|
|
input.focus();
|
|
input.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
input.value = "set value";
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runForceCommitTest: wrong event count #12");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #12");
|
|
is(events[0].target, input,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #12`);
|
|
is(events[1].type, "compositionend",
|
|
"runForceCommitTest: the 2nd event must be compositionend #12");
|
|
is(events[1].target, input,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #12`);
|
|
is(events[1].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #12");
|
|
ok(!getEditor(input).isComposing,
|
|
"runForceCommitTest: the input still has composition #12");
|
|
is(input.value, "set value",
|
|
"runForceCommitTest: the input doesn't have the set text #12");
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
textarea.value = "set value";
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runForceCommitTest: wrong event count #13");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #13");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #13`);
|
|
is(events[1].type, "compositionend",
|
|
"runForceCommitTest: the 2nd event must be compositionend #13");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #13`);
|
|
is(events[1].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #13");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runForceCommitTest: the textarea still has composition #13");
|
|
is(textarea.value, "set value",
|
|
"runForceCommitTest: the textarea doesn't have the set text #13");
|
|
|
|
input.focus();
|
|
input.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
input.value += " appended value";
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runForceCommitTest: wrong event count #14");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #14");
|
|
is(events[0].target, input,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #14`);
|
|
is(events[1].type, "compositionend",
|
|
"runForceCommitTest: the 2nd event must be compositionend #14");
|
|
is(events[1].target, input,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #14`);
|
|
is(events[1].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #14");
|
|
ok(!getEditor(input).isComposing,
|
|
"runForceCommitTest: the input still has composition #14");
|
|
is(input.value, "\u306E appended value",
|
|
"runForceCommitTest: the input should have both composed text and appended text #14");
|
|
|
|
input.focus();
|
|
input.value = "abcd";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
input.value = "abcd\u306E";
|
|
|
|
is(events.length, 0,
|
|
"runForceCommitTest: setting same value to input with composition shouldn't cause any events #15");
|
|
is(input.value, "abcd\u306E",
|
|
"runForceCommitTest: the input has unexpected value #15");
|
|
|
|
input.blur(); // commit composition
|
|
|
|
textarea.focus();
|
|
textarea.value = "abcd";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
textarea.value = "abcd\u306E";
|
|
|
|
is(events.length, 0,
|
|
"runForceCommitTest: setting same value to textarea with composition shouldn't cause any events #16");
|
|
is(textarea.value, "abcd\u306E",
|
|
"runForceCommitTest: the input has unexpected value #16");
|
|
|
|
textarea.blur(); // commit composition
|
|
|
|
window.removeEventListener("compositionstart", eventHandler, true);
|
|
window.removeEventListener("compositionupdate", eventHandler, true);
|
|
window.removeEventListener("compositionend", eventHandler, true);
|
|
window.removeEventListener("beforeinput", eventHandler, true);
|
|
window.removeEventListener("input", eventHandler, true);
|
|
window.removeEventListener("text", eventHandler, true);
|
|
}
|
|
|
|
function runNestedSettingValue()
|
|
{
|
|
let isTesting = false;
|
|
let events = [];
|
|
function eventHandler(aEvent)
|
|
{
|
|
events.push(aEvent);
|
|
if (isTesting) {
|
|
aEvent.target.value += aEvent.type + ", ";
|
|
}
|
|
}
|
|
window.addEventListener("compositionstart", eventHandler, true);
|
|
window.addEventListener("compositionupdate", eventHandler, true);
|
|
window.addEventListener("compositionend", eventHandler, true);
|
|
window.addEventListener("beforeinput", eventHandler, true);
|
|
window.addEventListener("input", eventHandler, true);
|
|
window.addEventListener("text", eventHandler, true);
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
isTesting = true;
|
|
textarea.value = "first setting value, ";
|
|
isTesting = false;
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runNestedSettingValue: wrong event count #1");
|
|
is(events[0].type, "text",
|
|
"runNestedSettingValue: the 1st event must be text #1");
|
|
is(events[0].target, textarea,
|
|
`runNestedSettingValue: The "${events[0].type}" event was fired on wrong event target #1`);
|
|
is(events[1].type, "compositionend",
|
|
"runNestedSettingValue: the 2nd event must be compositionend #1");
|
|
is(events[1].target, textarea,
|
|
`runNestedSettingValue: The "${events[1].type}" event was fired on wrong event target #1`);
|
|
is(events[1].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #1");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runNestedSettingValue: the textarea still has composition #1");
|
|
is(textarea.value, "first setting value, text, compositionend, ",
|
|
"runNestedSettingValue: the textarea should have all string set to value attribute");
|
|
|
|
input.focus();
|
|
input.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
isTesting = true;
|
|
input.value = "first setting value, ";
|
|
isTesting = false;
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runNestedSettingValue: wrong event count #2");
|
|
is(events[0].type, "text",
|
|
"runNestedSettingValue: the 1st event must be text #2");
|
|
is(events[0].target, input,
|
|
`runNestedSettingValue: The "${events[0].type}" event was fired on wrong event target #2`);
|
|
is(events[1].type, "compositionend",
|
|
"runNestedSettingValue: the 2nd event must be compositionend #2");
|
|
is(events[1].target, input,
|
|
`runNestedSettingValue: The "${events[1].type}" event was fired on wrong event target #2`);
|
|
is(events[1].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #2");
|
|
ok(!getEditor(input).isComposing,
|
|
"runNestedSettingValue: the input still has composition #2");
|
|
is(textarea.value, "first setting value, text, compositionend, ",
|
|
"runNestedSettingValue: the input should have all string set to value attribute #2");
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
isTesting = true;
|
|
textarea.setRangeText("first setting value, ");
|
|
isTesting = false;
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runNestedSettingValue: wrong event count #3");
|
|
is(events[0].type, "text",
|
|
"runNestedSettingValue: the 1st event must be text #3");
|
|
is(events[0].target, textarea,
|
|
`runNestedSettingValue: The ${events[0].type} event was fired on wrong event target #3`);
|
|
is(events[1].type, "compositionend",
|
|
"runNestedSettingValue: the 2nd event must be compositionend #3");
|
|
is(events[1].target, textarea,
|
|
`runNestedSettingValue: The ${events[1].type} event was fired on wrong event target #3`);
|
|
is(events[1].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #3");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runNestedSettingValue: the textarea still has composition #3");
|
|
is(textarea.value, "\u306Efirst setting value, text, compositionend, ",
|
|
"runNestedSettingValue: the textarea should have appended by setRangeText() and all string set to value attribute #3");
|
|
|
|
input.focus();
|
|
input.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
events = [];
|
|
isTesting = true;
|
|
input.setRangeText("first setting value, ");
|
|
isTesting = false;
|
|
|
|
// In this case, the result of committing composition won't appear since
|
|
// the new value overwrites it. Therefore, neither `beforeinput` nor `input`
|
|
// event is fired.
|
|
is(events.length, 2,
|
|
"runNestedSettingValue: wrong event count #4");
|
|
is(events[0].type, "text",
|
|
"runNestedSettingValue: the 1st event must be text #4");
|
|
is(events[0].target, input,
|
|
`runNestedSettingValue: The "${events[0].type}" event was fired on wrong event target #4`);
|
|
is(events[1].type, "compositionend",
|
|
"runNestedSettingValue: the 2nd event must be compositionend #4");
|
|
is(events[1].target, input,
|
|
`runNestedSettingValue: The "${events[1].type}" event was fired on wrong event target #4`);
|
|
is(events[1].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #4");
|
|
ok(!getEditor(input).isComposing,
|
|
"runNestedSettingValue: the input still has composition #4");
|
|
is(textarea.value, "\u306Efirst setting value, text, compositionend, ",
|
|
"runNestedSettingValue: the input should have all string appended by setRangeText() and set to value attribute #4");
|
|
|
|
window.removeEventListener("compositionstart", eventHandler, true);
|
|
window.removeEventListener("compositionupdate", eventHandler, true);
|
|
window.removeEventListener("compositionend", eventHandler, true);
|
|
window.removeEventListener("beforeinput", eventHandler, true);
|
|
window.removeEventListener("input", eventHandler, true);
|
|
window.removeEventListener("text", eventHandler, true);
|
|
|
|
}
|
|
|
|
async function runAsyncForceCommitTest()
|
|
{
|
|
let events;
|
|
function eventHandler(aEvent)
|
|
{
|
|
events.push(aEvent);
|
|
};
|
|
|
|
// If IME commits composition for a request, TextComposition commits
|
|
// composition automatically because most web apps must expect that active
|
|
// composition should be committed synchronously. Therefore, in this case,
|
|
// a click during composition should cause committing composition
|
|
// synchronously and delayed commit shouldn't cause composition events.
|
|
let commitRequested = false;
|
|
let onFinishTest = null;
|
|
function callback(aTIP, aNotification)
|
|
{
|
|
ok(true, aNotification.type);
|
|
if (aNotification.type != "request-to-commit") {
|
|
return true;
|
|
}
|
|
commitRequested = true;
|
|
if (onFinishTest) {
|
|
let resolve = onFinishTest;
|
|
onFinishTest = null;
|
|
|
|
SimpleTest.executeSoon(() => {
|
|
events = [];
|
|
aTIP.commitComposition();
|
|
|
|
is(events.length, 0,
|
|
"runAsyncForceCommitTest: composition events shouldn't been fired by asynchronous call of nsITextInputProcessor.commitComposition()");
|
|
|
|
SimpleTest.executeSoon(resolve);
|
|
});
|
|
}
|
|
return true;
|
|
};
|
|
|
|
function promiseCleanUp() {
|
|
return new Promise(resolve => { onFinishTest = resolve; });
|
|
}
|
|
|
|
window.addEventListener("compositionstart", eventHandler, true);
|
|
window.addEventListener("compositionupdate", eventHandler, true);
|
|
window.addEventListener("compositionend", eventHandler, true);
|
|
window.addEventListener("beforeinput", eventHandler, true);
|
|
window.addEventListener("input", eventHandler, true);
|
|
window.addEventListener("text", eventHandler, true);
|
|
|
|
// Make the composition in textarea commit by click in the textarea
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
events = [];
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
}, window, callback);
|
|
|
|
is(events.length, 5,
|
|
"runAsyncForceCommitTest: wrong event count #1");
|
|
is(events[0].type, "compositionstart",
|
|
"runAsyncForceCommitTest: the 1st event must be compositionstart #1");
|
|
is(events[1].type, "compositionupdate",
|
|
"runAsyncForceCommitTest: the 2nd event must be compositionupdate #1");
|
|
is(events[2].type, "text",
|
|
"runAsyncForceCommitTest: the 3rd event must be text #1");
|
|
is(events[3].type, "beforeinput",
|
|
"runAsyncForceCommitTest: the 4th event must be beforeinput #1");
|
|
checkInputEvent(events[3], true, "insertCompositionText", "\u306E", [],
|
|
"runAsyncForceCommitTest #1");
|
|
is(events[4].type, "input",
|
|
"runAsyncForceCommitTest: the 5th event must be input #1");
|
|
checkInputEvent(events[4], true, "insertCompositionText", "\u306E", [],
|
|
"runAsyncForceCommitTest #1");
|
|
|
|
events = [];
|
|
let waitCleanState = promiseCleanUp();
|
|
|
|
synthesizeMouseAtCenter(textarea, {});
|
|
|
|
ok(commitRequested,
|
|
"runAsyncForceCommitTest: \"request-to-commit\" should've been notified");
|
|
is(events.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
"runAsyncForceCommitTest: wrong event count #2");
|
|
let index = -1;
|
|
is(events[++index].type, "text",
|
|
`runAsyncForceCommitTest: events[${index}] must be text #2`);
|
|
is(events[index].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
is(events[++index].type, "beforeinput",
|
|
`runAsyncForceCommitTest: events[${index}] must be beforeinput #2`);
|
|
is(events[index].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
"runAsyncForceCommitTest #2");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(events[++index].type, "input",
|
|
`runAsyncForceCommitTest: events[${index}] must be input #2`);
|
|
checkInputEvent(events[index], true, "insertCompositionText", "\u306E", [],
|
|
`runAsyncForceCommitTest #2`);
|
|
}
|
|
is(events[++index].type, "compositionend",
|
|
`runAsyncForceCommitTest: events[${index}] must be compositionend #2`);
|
|
is(events[index].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
is(events[index].data, "\u306E",
|
|
`runAsyncForceCommitTest: compositionend has wrong data #2`);
|
|
is(events[++index].type, "input",
|
|
`runAsyncForceCommitTest: events[${index}] must be input #2`);
|
|
is(events[index].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[index].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[index], false, "insertCompositionText", "\u306E", [],
|
|
`runAsyncForceCommitTest #2`);
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runAsyncForceCommitTest: the textarea still has composition #2");
|
|
is(textarea.value, "\u306E",
|
|
"runAsyncForceCommitTest: the textarea doesn't have the committed text #2");
|
|
|
|
await waitCleanState;
|
|
|
|
window.removeEventListener("compositionstart", eventHandler, true);
|
|
window.removeEventListener("compositionupdate", eventHandler, true);
|
|
window.removeEventListener("compositionend", eventHandler, true);
|
|
window.removeEventListener("beforeinput", eventHandler, true);
|
|
window.removeEventListener("input", eventHandler, true);
|
|
window.removeEventListener("text", eventHandler, true);
|
|
}
|
|
|
|
function runBug811755Test()
|
|
{
|
|
iframe2.contentDocument.body.innerHTML = "<div>content<br/></div>";
|
|
iframe2.contentWindow.focus();
|
|
// Query everything
|
|
let textContent = synthesizeQueryTextContent(0, 10);
|
|
if (!checkQueryContentResult(textContent, "runBug811755Test: synthesizeQueryTextContent #1")) {
|
|
return;
|
|
}
|
|
// Query everything but specify exact end offset, which should be immediately after the <br> node
|
|
// If PreContentIterator is used, the next node after <br> is the node after </div>.
|
|
// If ContentIterator is used, the next node is the <div> node itself. In this case, the end
|
|
// node ends up being before the start node, and an empty string is returned.
|
|
let queryContent = synthesizeQueryTextContent(0, textContent.text.length);
|
|
if (!checkQueryContentResult(queryContent, "runBug811755Test: synthesizeQueryTextContent #2")) {
|
|
return;
|
|
}
|
|
is(queryContent.text, textContent.text, "runBug811755Test: two queried texts don't match");
|
|
}
|
|
|
|
function runIsComposingTest()
|
|
{
|
|
let expectedIsComposing = false;
|
|
let description = "";
|
|
|
|
function eventHandler(aEvent)
|
|
{
|
|
if (aEvent.type == "keydown" || aEvent.type == "keyup") {
|
|
is(aEvent.isComposing, expectedIsComposing,
|
|
"runIsComposingTest: " + description + " (type=" + aEvent.type + ", key=" + aEvent.key + ")");
|
|
} else if (aEvent.type == "keypress") {
|
|
// keypress event shouldn't be fired during composition so that isComposing should be always false.
|
|
is(aEvent.isComposing, false,
|
|
"runIsComposingTest: " + description + " (type=" + aEvent.type + ")");
|
|
} else {
|
|
checkInputEvent(aEvent, expectedIsComposing, "insertCompositionText", "\u3042", [],
|
|
`runIsComposingTest: ${description}`);
|
|
}
|
|
}
|
|
|
|
function onComposition(aEvent)
|
|
{
|
|
if (aEvent.type == "compositionstart") {
|
|
expectedIsComposing = true;
|
|
} else if (aEvent.type == "compositionend") {
|
|
expectedIsComposing = false;
|
|
}
|
|
}
|
|
|
|
textarea.addEventListener("keydown", eventHandler, true);
|
|
textarea.addEventListener("keypress", eventHandler, true);
|
|
textarea.addEventListener("keyup", eventHandler, true);
|
|
textarea.addEventListener("beforeinput", eventHandler, true);
|
|
textarea.addEventListener("input", eventHandler, true);
|
|
textarea.addEventListener("compositionstart", onComposition, true);
|
|
textarea.addEventListener("compositionend", onComposition, true);
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
// XXX These cases shouldn't occur in actual native key events because we
|
|
// don't dispatch key events while composition (bug 354358).
|
|
description = "events before dispatching compositionstart";
|
|
synthesizeKey("KEY_ArrowLeft");
|
|
|
|
description = "events after dispatching compositionchange";
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3042",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 },
|
|
"key": { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A },
|
|
});
|
|
|
|
// Although, firing keypress event during composition is a bug.
|
|
synthesizeKey("KEY_Insert");
|
|
|
|
description = "events for committing composition string";
|
|
|
|
synthesizeComposition({ type: "compositioncommitasis",
|
|
key: { key: "KEY_Enter", code: "Enter", type: "keydown" } });
|
|
|
|
// input event will be fired by synthesizing compositionend event.
|
|
// Then, its isComposing should be false.
|
|
description = "events after dispatching compositioncommitasis";
|
|
synthesizeKey("KEY_Enter", {type: "keyup"});
|
|
|
|
textarea.removeEventListener("keydown", eventHandler, true);
|
|
textarea.removeEventListener("keypress", eventHandler, true);
|
|
textarea.removeEventListener("keyup", eventHandler, true);
|
|
textarea.removeEventListener("beforeinput", eventHandler, true);
|
|
textarea.removeEventListener("input", eventHandler, true);
|
|
textarea.removeEventListener("compositionstart", onComposition, true);
|
|
textarea.removeEventListener("compositionend", onComposition, true);
|
|
|
|
textarea.value = "";
|
|
}
|
|
|
|
function runRedundantChangeTest()
|
|
{
|
|
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("input", handler, true);
|
|
textarea.addEventListener("text", handler, true);
|
|
|
|
textarea.value = "";
|
|
|
|
// synthesize change event
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3042",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
is(result.length, 4,
|
|
"runRedundantChangeTest: 4 events should be fired after synthesizing composition change #1");
|
|
is(result[0].type, "compositionupdate",
|
|
"runRedundantChangeTest: compositionupdate should be fired after synthesizing composition change #1");
|
|
is(result[1].type, "text",
|
|
"runRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string #1");
|
|
is(result[2].type, "beforeinput",
|
|
"runRedundantChangeTest: beforeinput should be fired after synthesizing composition change #1");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "\u3042", [],
|
|
"runRedundantChangeTest: after synthesizing composition change #1");
|
|
is(result[3].type, "input",
|
|
"runRedundantChangeTest: input should be fired after synthesizing composition change #1");
|
|
checkInputEvent(result[3], true, "insertCompositionText", "\u3042", [],
|
|
"runRedundantChangeTest: after synthesizing composition change #1");
|
|
is(textarea.value, "\u3042", "runRedundantChangeTest: textarea has uncommitted string #1");
|
|
|
|
// synthesize another change event
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3042\u3044",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
is(result.length, 4,
|
|
"runRedundantChangeTest: 4 events should be fired after synthesizing composition change #2");
|
|
is(result[0].type, "compositionupdate",
|
|
"runRedundantChangeTest: compositionupdate should be fired after synthesizing composition change #2");
|
|
is(result[1].type, "text",
|
|
"runRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string #2");
|
|
is(result[2].type, "beforeinput",
|
|
"runRedundantChangeTest: beforeinput should be fired after synthesizing composition change #2");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "\u3042\u3044", [],
|
|
"runRedundantChangeTest: after synthesizing composition change #2");
|
|
is(result[3].type, "input",
|
|
"runRedundantChangeTest: input should be fired after synthesizing composition change #2");
|
|
checkInputEvent(result[3], true, "insertCompositionText", "\u3042\u3044", [],
|
|
"runRedundantChangeTest: after synthesizing composition change #2");
|
|
is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has uncommitted string #2");
|
|
|
|
// synthesize same change event again
|
|
(() => {
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3042\u3044",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
is(result.length, 0, "runRedundantChangeTest: no events should be fired after synthesizing composition change again");
|
|
is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has uncommitted string #3");
|
|
|
|
// synthesize commit-as-is
|
|
clearResult();
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
is(result.length, kExpectInputBeforeCompositionEnd ? 5 : 4,
|
|
`runRedundantChangeTest: ${kExpectInputBeforeCompositionEnd ? 5 : 4} events should be fired after synthesizing composition commit-as-is`);
|
|
let index = -1;
|
|
is(result[++index].type, "text",
|
|
"runRedundantChangeTest: text should be fired after synthesizing composition commit-as-is for removing the ranges");
|
|
is(result[++index].type, "beforeinput",
|
|
"runRedundantChangeTest: beforeinput should be fired after synthesizing composition commit-as-is for removing the ranges");
|
|
checkInputEvent(result[index], true, "insertCompositionText", "\u3042\u3044", [],
|
|
"runRedundantChangeTest: at synthesizing commit-as-is");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(result[++index].type, "input",
|
|
"runRedundantChangeTest: input should be fired before compositionend at synthesizing commit-as-is");
|
|
checkInputEvent(result[index], true, "insertCompositionText", "\u3042\u3044", [],
|
|
"runRedundantChangeTest: at synthesizing commit-as-is");
|
|
}
|
|
is(result[++index].type, "compositionend",
|
|
"runRedundantChangeTest: compositionend should be fired after synthesizing composition commit-as-is");
|
|
is(result[++index].type, "input",
|
|
"runRedundantChangeTest: input should be fired before compositionend at synthesizing commit-as-is");
|
|
checkInputEvent(result[index], false, "insertCompositionText", "\u3042\u3044", [],
|
|
"runRedundantChangeTest: at synthesizing commit-as-is");
|
|
is(textarea.value, "\u3042\u3044", "runRedundantChangeTest: textarea has the commit string");
|
|
})();
|
|
|
|
textarea.removeEventListener("compositionupdate", handler, true);
|
|
textarea.removeEventListener("compositionend", handler, true);
|
|
textarea.removeEventListener("beforeinput", handler, true);
|
|
textarea.removeEventListener("input", handler, true);
|
|
textarea.removeEventListener("text", handler, true);
|
|
}
|
|
|
|
function runNotRedundantChangeTest()
|
|
{
|
|
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("input", handler, true);
|
|
textarea.addEventListener("text", handler, true);
|
|
|
|
textarea.value = "abcde";
|
|
|
|
// synthesize change event with non-null ranges
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABCDE",
|
|
"clauses":
|
|
[
|
|
{ "length": 5, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 }
|
|
});
|
|
|
|
is(result.length, 4,
|
|
"runNotRedundantChangeTest: 4 events should be fired after synthesizing composition change with non-null ranges");
|
|
is(result[0].type, "compositionupdate",
|
|
"runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition change with non-null ranges");
|
|
is(result[1].type, "text",
|
|
"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, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition change with non-null ranges");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "ABCDE", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges");
|
|
is(result[3].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges");
|
|
checkInputEvent(result[3], true, "insertCompositionText", "ABCDE", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges");
|
|
is(textarea.value, "abcdeABCDE", "runNotRedundantChangeTest: textarea has uncommitted string #1");
|
|
|
|
// synthesize change event with null ranges
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABCDE",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
});
|
|
is(result.length, 3,
|
|
"runNotRedundantChangeTest: 3 events should be fired after synthesizing composition change with null ranges after non-null ranges");
|
|
is(result[0].type, "text",
|
|
"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, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition change because it's dispatched when there is composing string with null ranges after non-null ranges");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "ABCDE", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with null ranges after non-null ranges");
|
|
is(result[2].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with null ranges after non-null ranges");
|
|
checkInputEvent(result[2], 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");
|
|
|
|
// synthesize change event with non-null ranges
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABCDE",
|
|
"clauses":
|
|
[
|
|
{ "length": 5, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 }
|
|
});
|
|
|
|
is(result.length, 3,
|
|
"runNotRedundantChangeTest: 3 events should be fired after synthesizing composition change with null ranges after non-null ranges");
|
|
is(result[0].type, "text",
|
|
"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, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition change because it's dispatched when there is composing string with non-null ranges after null ranges");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "ABCDE", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges after null ranges");
|
|
is(result[2].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges after null ranges");
|
|
checkInputEvent(result[2], 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");
|
|
|
|
// synthesize change event with empty data and null ranges
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "",
|
|
"clauses":
|
|
[
|
|
{ "length": 0, "attr": 0 }
|
|
]
|
|
},
|
|
});
|
|
is(result.length, 4,
|
|
"runNotRedundantChangeTest: 4 events should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
|
|
is(result[0].type, "compositionupdate",
|
|
"runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
|
|
is(result[1].type, "text",
|
|
"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, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with empty data and null ranges after non-null ranges");
|
|
is(result[3].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with empty data and null ranges after non-null ranges");
|
|
checkInputEvent(result[3], 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");
|
|
|
|
// synthesize change event with non-null ranges
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABCDE",
|
|
"clauses":
|
|
[
|
|
{ "length": 5, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 }
|
|
});
|
|
|
|
is(result.length, 4,
|
|
"runNotRedundantChangeTest: 4 events should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
|
|
is(result[0].type, "compositionupdate",
|
|
"runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
|
|
is(result[1].type, "text",
|
|
"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, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "ABCDE", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with non-null ranges after empty data and null ranges");
|
|
is(result[3].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after synthesizing composition change with non-null ranges after empty data and null ranges");
|
|
checkInputEvent(result[3], 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");
|
|
|
|
(() => {
|
|
clearResult();
|
|
synthesizeComposition({ type: "compositioncommit", data: "" });
|
|
|
|
is(result.length, kExpectInputBeforeCompositionEnd ? 6 : 5,
|
|
`runNotRedundantChangeTest: ${
|
|
kExpectInputBeforeCompositionEnd ? 6 : 5
|
|
} events should be fired after synthesizing composition commit with empty data after non-empty data`);
|
|
let index = -1;
|
|
is(result[++index].type, "compositionupdate",
|
|
"runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
is(result[++index].type, "text",
|
|
"runNotRedundantChangeTest: text should be fired after synthesizing composition change because it's dispatched when there is composing string with empty data after non-empty data");
|
|
is(result[++index].type, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
checkInputEvent(result[index], true, "insertCompositionText", "", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with empty data after non-empty data");
|
|
if (kExpectInputBeforeCompositionEnd) {
|
|
is(result[++index].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after compositionend after synthesizing composition change with empty data after non-empty data");
|
|
checkInputEvent(result[index], true, "insertCompositionText", "", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with empty data after non-empty data");
|
|
}
|
|
is(result[++index].type, "compositionend",
|
|
"runNotRedundantChangeTest: compositionend should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
is(result[++index].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after compositionend after synthesizing composition change with empty data after non-empty data");
|
|
checkInputEvent(result[index], 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");
|
|
})();
|
|
|
|
textarea.removeEventListener("compositionupdate", handler, true);
|
|
textarea.removeEventListener("compositionend", handler, true);
|
|
textarea.removeEventListener("beforeinput", handler, true);
|
|
textarea.removeEventListener("input", handler, true);
|
|
textarea.removeEventListener("text", handler, true);
|
|
}
|
|
|
|
function runNativeLineBreakerTest()
|
|
{
|
|
textarea.focus();
|
|
|
|
let result = {};
|
|
function clearResult()
|
|
{
|
|
result = { compositionupdate: null, compositionend: null };
|
|
}
|
|
|
|
function handler(aEvent)
|
|
{
|
|
result[aEvent.type] = aEvent.data;
|
|
}
|
|
|
|
Services.prefs.setBoolPref("dom.compositionevent.allow_control_characters", false);
|
|
|
|
textarea.addEventListener("compositionupdate", handler, true);
|
|
textarea.addEventListener("compositionend", handler, true);
|
|
|
|
// '\n' in composition string shouldn't be changed.
|
|
clearResult();
|
|
textarea.value = "";
|
|
let clauses = [ "abc\n", "def\n\ng", "hi\n", "\njkl" ];
|
|
let caret = clauses[0] + clauses[1] + clauses[2];
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": clauses.join(''),
|
|
"clauses":
|
|
[
|
|
{ "length": clauses[0].length,
|
|
"attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
|
{ "length": clauses[1].length,
|
|
"attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
|
{ "length": clauses[2].length,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": clauses[3].length,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
]
|
|
},
|
|
"caret": { "start": caret.length, "length": 0 }
|
|
});
|
|
|
|
checkSelection(caret.replace(/\n/g, "\n").length + (kLFLen - 1) * 4, "", "runNativeLineBreakerTest", "#1");
|
|
checkIMESelection("RawClause", true, 0, clauses[0].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1");
|
|
checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\n/g, kLF).length, clauses[1].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1");
|
|
checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\n/g, kLF).length, clauses[2].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1");
|
|
checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\n/g, kLF).length, clauses[3].replace(/\n/g, kLF), "runNativeLineBreakerTest: \\n shouldn't be replaced with any character #1");
|
|
is(result.compositionupdate, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in compositionupdate.data shouldn't be removed nor replaced with other characters #1");
|
|
is(textarea.value, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in textarea.value shouldn't be removed nor replaced with other characters #1");
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data: clauses.join('') });
|
|
checkSelection(clauses.join('').replace(/\n/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#2");
|
|
is(result.compositionend, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in compositionend.data shouldn't be removed nor replaced with other characters #2");
|
|
is(textarea.value, clauses.join('').replace(/\n/g, "\n"), "runNativeLineBreakerTest: \\n in textarea.value shouldn't be removed nor replaced with other characters #2");
|
|
|
|
// \r\n in composition string should be replaced with \n.
|
|
clearResult();
|
|
textarea.value = "";
|
|
clauses = [ "abc\r\n", "def\r\n\r\ng", "hi\r\n", "\r\njkl" ];
|
|
caret = clauses[0] + clauses[1] + clauses[2];
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": clauses.join(''),
|
|
"clauses":
|
|
[
|
|
{ "length": clauses[0].length,
|
|
"attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
|
{ "length": clauses[1].length,
|
|
"attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
|
{ "length": clauses[2].length,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": clauses[3].length,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
]
|
|
},
|
|
"caret": { "start": caret.length, "length": 0 }
|
|
});
|
|
|
|
checkSelection(caret.replace(/\r\n/g, "\n").length + (kLFLen - 1) * 4, "", "runNativeLineBreakerTest", "#3");
|
|
checkIMESelection("RawClause", true, 0, clauses[0].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3");
|
|
checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\r\n/g, kLF).length, clauses[1].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3");
|
|
checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\r\n/g, kLF).length, clauses[2].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3");
|
|
checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\r\n/g, kLF).length, clauses[3].replace(/\r\n/g, kLF), "runNativeLineBreakerTest: \\r\\n should be replaced with \\n #3");
|
|
is(result.compositionupdate, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in compositionudpate.data should be replaced with \\n #3");
|
|
is(textarea.value, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in textarea.value should be replaced with \\n #3");
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data: clauses.join('') });
|
|
checkSelection(clauses.join('').replace(/\r\n/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#4");
|
|
is(result.compositionend, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in compositionend.data should be replaced with \\n #4");
|
|
is(textarea.value, clauses.join('').replace(/\r\n/g, "\n"), "runNativeLineBreakerTest: \\r\\n in textarea.value should be replaced with \\n #4");
|
|
|
|
// \r (not followed by \n) in composition string should be replaced with \n.
|
|
clearResult();
|
|
textarea.value = "";
|
|
clauses = [ "abc\r", "def\r\rg", "hi\r", "\rjkl" ];
|
|
caret = clauses[0] + clauses[1] + clauses[2];
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": clauses.join(''),
|
|
"clauses":
|
|
[
|
|
{ "length": clauses[0].length,
|
|
"attr": COMPOSITION_ATTR_RAW_CLAUSE },
|
|
{ "length": clauses[1].length,
|
|
"attr": COMPOSITION_ATTR_SELECTED_RAW_CLAUSE },
|
|
{ "length": clauses[2].length,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": clauses[3].length,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
]
|
|
},
|
|
"caret": { "start": caret.length, "length": 0 }
|
|
});
|
|
|
|
checkSelection(caret.replace(/\r/g, "\n").length + (kLFLen - 1) * 4, "", "runNativeLineBreakerTest", "#5");
|
|
checkIMESelection("RawClause", true, 0, clauses[0].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5");
|
|
checkIMESelection("SelectedRawClause", true, clauses[0].replace(/\r/g, kLF).length, clauses[1].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5");
|
|
checkIMESelection("ConvertedClause", true, (clauses[0] + clauses[1]).replace(/\r/g, kLF).length, clauses[2].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5");
|
|
checkIMESelection("SelectedClause", true, (clauses[0] + clauses[1] + clauses[2]).replace(/\r/g, kLF).length, clauses[3].replace(/\r/g, kLF), "runNativeLineBreakerTest: \\r should be replaced with \\n #5");
|
|
is(result.compositionupdate, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in compositionupdate.data should be replaced with \\n #5");
|
|
is(textarea.value, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in textarea.value should be replaced with \\n #5");
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data: clauses.join('') });
|
|
checkSelection(clauses.join('').replace(/\r/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#6");
|
|
is(result.compositionend, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in compositionend.data should be replaced with \\n #6");
|
|
is(textarea.value, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in textarea.value should be replaced with \\n #6");
|
|
|
|
textarea.removeEventListener("compositionupdate", handler, true);
|
|
textarea.removeEventListener("compositionend", handler, true);
|
|
|
|
Services.prefs.clearUserPref("dom.compositionevent.allow_control_characters");
|
|
}
|
|
|
|
function runControlCharTest()
|
|
{
|
|
textarea.focus();
|
|
|
|
let result = {};
|
|
function clearResult()
|
|
{
|
|
result = { compositionupdate: null, compositionend: null };
|
|
}
|
|
|
|
function handler(aEvent)
|
|
{
|
|
result[aEvent.type] = aEvent.data;
|
|
}
|
|
|
|
textarea.addEventListener("compositionupdate", handler, true);
|
|
textarea.addEventListener("compositionend", handler, true);
|
|
|
|
textarea.value = "";
|
|
|
|
let controlChars = String.fromCharCode.apply(null, Object.keys(Array.from({length:0x20}))) + "\x7F";
|
|
let allowedChars = "\t\n\n";
|
|
|
|
let data = "AB" + controlChars + "CD" + controlChars + "EF";
|
|
let removedData = "AB" + allowedChars + "CD" + allowedChars + "EF";
|
|
|
|
let DIndex = data.indexOf("D");
|
|
let removedDIndex = removedData.indexOf("D");
|
|
|
|
// input string contains control characters
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": data,
|
|
"clauses":
|
|
[
|
|
{ "length": DIndex,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": data.length - DIndex,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": DIndex, "length": 0 }
|
|
});
|
|
|
|
checkSelection(removedDIndex + (kLFLen - 1) * 2, "", "runControlCharTest", "#1")
|
|
|
|
is(result.compositionupdate, removedData, "runControlCharTest: control characters in event.data should be removed in compositionupdate event #1");
|
|
is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #1");
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data });
|
|
|
|
is(result.compositionend, removedData, "runControlCharTest: control characters in event.data should be removed in compositionend event #2");
|
|
is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #2");
|
|
|
|
textarea.value = "";
|
|
|
|
clearResult();
|
|
|
|
Services.prefs.setBoolPref("dom.compositionevent.allow_control_characters", true);
|
|
|
|
// input string contains control characters, allowing control characters
|
|
clearResult();
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": data,
|
|
"clauses":
|
|
[
|
|
{ "length": DIndex,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": data.length - DIndex,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": DIndex, "length": 0 }
|
|
});
|
|
|
|
checkSelection(DIndex + (kLFLen - 1) * 2, "", "runControlCharTest", "#3")
|
|
|
|
is(result.compositionupdate, data.replace(/\r/g, "\n"), "runControlCharTest: control characters in event.data should not be removed in compositionupdate event #3");
|
|
is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #3");
|
|
|
|
synthesizeComposition({ type: "compositioncommit", data });
|
|
|
|
is(result.compositionend, data.replace(/\r/g, "\n"), "runControlCharTest: control characters in event.data should not be removed in compositionend event #4");
|
|
is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #4");
|
|
|
|
Services.prefs.clearUserPref("dom.compositionevent.allow_control_characters");
|
|
|
|
textarea.removeEventListener("compositionupdate", handler, true);
|
|
textarea.removeEventListener("compositionend", handler, true);
|
|
}
|
|
|
|
async function runRemoveContentTest()
|
|
{
|
|
let events = [];
|
|
function eventHandler(aEvent)
|
|
{
|
|
events.push(aEvent);
|
|
}
|
|
textarea.addEventListener("compositionstart", eventHandler, true);
|
|
textarea.addEventListener("compositionupdate", eventHandler, true);
|
|
textarea.addEventListener("compositionend", eventHandler, true);
|
|
textarea.addEventListener("beforeinput", eventHandler, true);
|
|
textarea.addEventListener("input", eventHandler, true);
|
|
textarea.addEventListener("text", eventHandler, true);
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u306E",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
let nextSibling = textarea.nextSibling;
|
|
let parent = textarea.parentElement;
|
|
|
|
events = [];
|
|
parent.removeChild(textarea);
|
|
|
|
await waitForEventLoops(50);
|
|
|
|
// XXX Currently, "input" event and "beforeinput" event are not fired on removed content.
|
|
is(events.length, 3,
|
|
"runRemoveContentTest: wrong event count #1");
|
|
is(events[0].type, "compositionupdate",
|
|
"runRemoveContentTest: the 1st event must be compositionupdate #1");
|
|
is(events[0].target, textarea,
|
|
`runRemoveContentTest: The "${events[0].type}" event was fired on wrong event target #1`);
|
|
is(events[0].data, "",
|
|
"runRemoveContentTest: compositionupdate has wrong data #1");
|
|
is(events[1].type, "text",
|
|
"runRemoveContentTest: the 2nd event must be text #1");
|
|
is(events[1].target, textarea,
|
|
`runRemoveContentTest: The "${events[1].type}" event was fired on wrong event target #1`);
|
|
todo_is(events[2].type, "beforeinput",
|
|
"runRemoveContentTest: the 3rd event must be beforeinput #1");
|
|
// is(events[2].target, textarea,
|
|
// `runRemoveContentTest: The "${events[2].type}" event was fired on wrong event target #1`);
|
|
// checkInputEvent(events[2], true, "insertCompositionText", "", [],
|
|
// "runRemoveContentTest: #1");
|
|
is(events[2].type, "compositionend",
|
|
"runRemoveContentTest: the 4th event must be compositionend #1");
|
|
is(events[2].target, textarea,
|
|
`runRemoveContentTest: The "${events[2].type}" event was fired on wrong event target #1`);
|
|
is(events[2].data, "",
|
|
"runRemoveContentTest: compositionend has wrong data #1");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runRemoveContentTest: the textarea still has composition #1");
|
|
todo_is(textarea.value, "",
|
|
"runRemoveContentTest: the textarea has the committed text? #1");
|
|
|
|
parent.insertBefore(textarea, nextSibling);
|
|
|
|
textarea.focus();
|
|
textarea.value = "";
|
|
|
|
synthesizeComposition({ type: "compositionstart" });
|
|
|
|
events = [];
|
|
parent.removeChild(textarea);
|
|
|
|
await waitForEventLoops(50);
|
|
|
|
// XXX Currently, "input" event and "beforeinput" event are not fired on removed content.
|
|
is(events.length, 2,
|
|
"runRemoveContentTest: wrong event count #2");
|
|
is(events[0].type, "text",
|
|
"runRemoveContentTest: the 1st event must be text #2");
|
|
is(events[0].target, textarea,
|
|
`runRemoveContentTest: The ${events[0].type} event was fired on wrong event target #2`);
|
|
todo_is(events[1].type, "beforeinput",
|
|
"runRemoveContentTest: the 2nd event must be beforeinput #2");
|
|
// is(events[1].target, textarea,
|
|
// `runRemoveContentTest: The ${events[1].type} event was fired on wrong event target #2`);
|
|
// checkInputEvent(events[1], true, "insertCompositionText", "", [],
|
|
// "runRemoveContentTest: #2");
|
|
is(events[1].type, "compositionend",
|
|
"runRemoveContentTest: the 3rd event must be compositionend #2");
|
|
is(events[1].target, textarea,
|
|
`runRemoveContentTest: The ${events[1].type} event was fired on wrong event target #2`);
|
|
is(events[1].data, "",
|
|
"runRemoveContentTest: compositionupdate has wrong data #2");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runRemoveContentTest: the textarea still has composition #2");
|
|
is(textarea.value, "",
|
|
"runRemoveContentTest: the textarea has the committed text? #2");
|
|
|
|
parent.insertBefore(textarea, nextSibling);
|
|
|
|
textarea.removeEventListener("compositionstart", eventHandler, true);
|
|
textarea.removeEventListener("compositionupdate", eventHandler, true);
|
|
textarea.removeEventListener("compositionend", eventHandler, true);
|
|
textarea.removeEventListener("beforeinput", eventHandler, true);
|
|
textarea.removeEventListener("input", eventHandler, true);
|
|
textarea.removeEventListener("text", eventHandler, true);
|
|
|
|
await waitForTick();
|
|
}
|
|
|
|
function runTestOnAnotherContext(aPanelOrFrame, aFocusedEditor, aTestName)
|
|
{
|
|
aFocusedEditor.value = "";
|
|
|
|
// The frames and panel are cross-origin, and we no longer
|
|
// propagate flushes to parent cross-origin iframes explicitly,
|
|
// so flush our own layout here so the positions are correct.
|
|
document.documentElement.getBoundingClientRect();
|
|
|
|
let editorRect = synthesizeQueryEditorRect();
|
|
if (!checkQueryContentResult(editorRect, aTestName + ": editorRect")) {
|
|
return;
|
|
}
|
|
|
|
let r = aPanelOrFrame.getBoundingClientRect();
|
|
let parentRect = {
|
|
left: r.left * window.devicePixelRatio,
|
|
top: r.top * window.devicePixelRatio,
|
|
width: (r.right - r.left) * window.devicePixelRatio,
|
|
height: (r.bottom - r.top) * window.devicePixelRatio,
|
|
};
|
|
checkRectContainsRect(editorRect, parentRect, aTestName +
|
|
": the editor rect coordinates are wrong");
|
|
|
|
// input characters
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3078\u3093\u3057\u3093",
|
|
"clauses":
|
|
[
|
|
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3078\u3093\u3057\u3093", aTestName, "#1-1") ||
|
|
!checkSelection(4, "", aTestName, "#1-1")) {
|
|
return;
|
|
}
|
|
|
|
// convert them #1
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u8FD4\u4FE1",
|
|
"clauses":
|
|
[
|
|
{ "length": 2,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u8FD4\u4FE1", aTestName, "#1-2") ||
|
|
!checkSelection(2, "", aTestName, "#1-2")) {
|
|
return;
|
|
}
|
|
|
|
// convert them #2
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u5909\u8EAB",
|
|
"clauses":
|
|
[
|
|
{ "length": 2,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u5909\u8EAB", aTestName, "#1-3") ||
|
|
!checkSelection(2, "", aTestName, "#1-3")) {
|
|
return;
|
|
}
|
|
|
|
// commit them
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
if (!checkContent("\u5909\u8EAB", aTestName, "#1-4") ||
|
|
!checkSelection(2, "", aTestName, "#1-4")) {
|
|
return;
|
|
}
|
|
|
|
is(aFocusedEditor.value, "\u5909\u8EAB",
|
|
aTestName + ": composition isn't in the focused editor");
|
|
if (aFocusedEditor.value != "\u5909\u8EAB") {
|
|
return;
|
|
}
|
|
|
|
let textRect = synthesizeQueryTextRect(0, 1);
|
|
let caretRect = synthesizeQueryCaretRect(2);
|
|
if (!checkQueryContentResult(textRect,
|
|
aTestName + ": synthesizeQueryTextRect") ||
|
|
!checkQueryContentResult(caretRect,
|
|
aTestName + ": synthesizeQueryCaretRect")) {
|
|
return;
|
|
}
|
|
checkRectContainsRect(textRect, editorRect, aTestName + ":testRect");
|
|
checkRectContainsRect(caretRect, editorRect, aTestName + ":caretRect");
|
|
}
|
|
|
|
function runFrameTest()
|
|
{
|
|
textareaInFrame.focus();
|
|
runTestOnAnotherContext(iframe, textareaInFrame, "runFrameTest");
|
|
runCharAtPointTest(textareaInFrame, "textarea in the iframe");
|
|
}
|
|
|
|
async function runPanelTest()
|
|
{
|
|
panel.hidden = false;
|
|
let waitOpenPopup = new Promise(resolve => {
|
|
panel.addEventListener("popupshown", resolve, {once: true});
|
|
});
|
|
let waitFocusTextBox = new Promise(resolve => {
|
|
textbox.addEventListener("focus", resolve, {once: true});
|
|
});
|
|
panel.openPopupAtScreen(window.screenX + window.outerWidth, 0, false);
|
|
await waitOpenPopup;
|
|
textbox.focus();
|
|
await waitFocusTextBox;
|
|
is(panel.state, "open", "The panel should be open (after textbox.focus())");
|
|
await waitForTick();
|
|
is(panel.state, "open", "The panel should be open (after waitForTick())");
|
|
runTestOnAnotherContext(panel, textbox, "runPanelTest");
|
|
is(panel.state, "open", "The panel should be open (after runTestOnAnotherContext())");
|
|
runCharAtPointTest(textbox, "textbox in the panel");
|
|
is(panel.state, "open", "The panel should be open (after runCharAtPointTest())");
|
|
let waitClosePopup = new Promise(resolve => {
|
|
panel.addEventListener("popuphidden", resolve, {once: true});
|
|
});
|
|
panel.hidePopup();
|
|
await waitClosePopup;
|
|
await waitForTick();
|
|
}
|
|
|
|
// eslint-disable-next-line complexity
|
|
function runMaxLengthTest()
|
|
{
|
|
input.maxLength = 1;
|
|
input.value = "";
|
|
input.focus();
|
|
|
|
let kDesc ="runMaxLengthTest";
|
|
|
|
// input first character
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089", kDesc, "#1-1") ||
|
|
!checkSelection(1, "", kDesc, "#1-1")) {
|
|
return;
|
|
}
|
|
|
|
// input second character
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC", kDesc, "#1-2") ||
|
|
!checkSelection(2, "", kDesc, "#1-2")) {
|
|
return;
|
|
}
|
|
|
|
// input third character
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081", kDesc, "#1-3") ||
|
|
!checkSelection(3, "", kDesc, "#1-3")) {
|
|
return;
|
|
}
|
|
|
|
// input fourth character
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093",
|
|
"clauses":
|
|
[
|
|
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093", kDesc, "#1-4") ||
|
|
!checkSelection(4, "", kDesc, "#1-4")) {
|
|
return;
|
|
}
|
|
|
|
|
|
// backspace
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081", kDesc, "#1-5") ||
|
|
!checkSelection(3, "", kDesc, "#1-5")) {
|
|
return;
|
|
}
|
|
|
|
// re-input
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093",
|
|
"clauses":
|
|
[
|
|
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093", kDesc, "#1-6") ||
|
|
!checkSelection(4, "", kDesc, "#1-6")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055",
|
|
"clauses":
|
|
[
|
|
{ "length": 5, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 5, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055", kDesc, "#1-7") ||
|
|
!checkSelection(5, "", kDesc, "#1-7")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044",
|
|
"clauses":
|
|
[
|
|
{ "length": 6, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 6, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044", kDesc, "#1-8") ||
|
|
!checkSelection(6, "", kDesc, "#1-8")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
|
|
"clauses":
|
|
[
|
|
{ "length": 7, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 7, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053",
|
|
kDesc, "#1-8") ||
|
|
!checkSelection(7, "", kDesc, "#1-8")) {
|
|
return;
|
|
}
|
|
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
|
|
"clauses":
|
|
[
|
|
{ "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 8, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
|
|
kDesc, "#1-9") ||
|
|
!checkSelection(8, "", kDesc, "#1-9")) {
|
|
return;
|
|
}
|
|
|
|
// convert
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
|
|
"clauses":
|
|
[
|
|
{ "length": 4,
|
|
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 2,
|
|
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 4, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8", kDesc, "#1-10") ||
|
|
!checkSelection(4, "", kDesc, "#1-10")) {
|
|
return;
|
|
}
|
|
|
|
// commit the composition string
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
if (!checkContent("\u30E9", kDesc, "#1-11") ||
|
|
!checkSelection(1, "", kDesc, "#1-11")) {
|
|
return;
|
|
}
|
|
|
|
// input characters
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u3057",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u30E9\u3057", kDesc, "#2-1") ||
|
|
!checkSelection(1 + 1, "", kDesc, "#2-1")) {
|
|
return;
|
|
}
|
|
|
|
// commit the composition string
|
|
synthesizeComposition({ type: "compositioncommit", data: "\u3058" });
|
|
if (!checkContent("\u30E9", kDesc, "#2-2") ||
|
|
!checkSelection(1 + 0, "", kDesc, "#2-2")) {
|
|
return;
|
|
}
|
|
|
|
// Undo
|
|
synthesizeKey("Z", {accelKey: true});
|
|
|
|
// XXX this is unexpected behavior, see bug 258291
|
|
if (!checkContent("\u30E9", kDesc, "#3-1") ||
|
|
!checkSelection(1 + 0, "", kDesc, "#3-1")) {
|
|
return;
|
|
}
|
|
|
|
// Undo
|
|
synthesizeKey("Z", {accelKey: true});
|
|
if (!checkContent("", kDesc, "#3-2") ||
|
|
!checkSelection(0, "", kDesc, "#3-2")) {
|
|
return;
|
|
}
|
|
|
|
// Redo
|
|
synthesizeKey("Z", {accelKey: true, shiftKey: true});
|
|
if (!checkContent("\u30E9", kDesc, "#3-3") ||
|
|
!checkSelection(1, "", kDesc, "#3-3")) {
|
|
return;
|
|
}
|
|
|
|
// Redo
|
|
synthesizeKey("Z", {accelKey: true, shiftKey: true});
|
|
if (!checkContent("\u30E9", kDesc, "#3-4") ||
|
|
!checkSelection(1 + 0, "", kDesc, "#3-4")) {
|
|
return;
|
|
}
|
|
|
|
// The input element whose content length is already maxlength and
|
|
// the carest is at start of the content.
|
|
input.value = "X";
|
|
input.selectionStart = input.selectionEnd = 0;
|
|
|
|
// input characters
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u9B54",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u9B54X", kDesc, "#4-1") ||
|
|
!checkSelection(1, "", kDesc, "#4-1")) {
|
|
return;
|
|
}
|
|
|
|
// commit the composition string
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
// The input text must be discarded. Then, the caret position shouldn't be
|
|
// updated from its position at compositionstart.
|
|
if (!checkContent("X", kDesc, "#4-2") ||
|
|
!checkSelection(0, "", kDesc, "#4-2")) {
|
|
return;
|
|
}
|
|
|
|
// input characters
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "\u9B54\u6CD5",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
|
|
if (!checkContent("\u9B54\u6CD5X", kDesc, "#5-1") ||
|
|
!checkSelection(2, "", kDesc, "#5-1")) {
|
|
return;
|
|
}
|
|
|
|
// commit the composition string
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
|
|
if (checkContent("X", kDesc, "#5-2")) {
|
|
checkSelection(0, "", kDesc, "#5-2");
|
|
}
|
|
}
|
|
|
|
async function runEditorReframeTests()
|
|
{
|
|
async function runEditorReframeTest(aEditor, aWindow, aEventType)
|
|
{
|
|
function getValue()
|
|
{
|
|
return aEditor == contenteditable ?
|
|
aEditor.innerHTML.replace("<br>", "") : aEditor.value;
|
|
}
|
|
|
|
let description = "runEditorReframeTest(" + aEditor.id + ", \"" + aEventType + "\"): ";
|
|
|
|
let tests = [
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "a",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "a", description + "Typing 'a'");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ab",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ab", description + "Typing 'b' next to 'a'");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "abc",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "abc", description + "Typing 'c' next to 'ab'");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "abc",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "abc", description + "Starting to convert 'ab][c'");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABc",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABc", description + "Starting to convert 'AB][c'");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ABC",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_CONVERTED_CLAUSE },
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABC", description + "Starting to convert 'AB][C'");
|
|
},
|
|
},
|
|
{ test () {
|
|
// Commit composition
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABC", description + "Committed as 'ABC'");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "d",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABCd", description + "Typing 'd' next to ABC");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "de",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABCde", description + "Typing 'e' next to ABCd");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "def",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABCdef", description + "Typing 'f' next to ABCde");
|
|
},
|
|
},
|
|
{ test () {
|
|
// Commit composition
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABCdef", description + "Commit 'def' without convert");
|
|
},
|
|
},
|
|
{ test () {
|
|
// Select "Cd"
|
|
synthesizeKey("KEY_ArrowLeft");
|
|
synthesizeKey("KEY_ArrowLeft");
|
|
synthesizeKey("KEY_Shift", {type: "keydown", shiftKey: true});
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
|
|
synthesizeKey("KEY_Shift", {type: "keyup"});
|
|
},
|
|
check () {
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "g",
|
|
"clauses":
|
|
[
|
|
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 1, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABgef", description + "Typing 'g' next to AB");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "gh",
|
|
"clauses":
|
|
[
|
|
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 2, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABghef", description + "Typing 'h' next to ABg");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "ghi",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABghief", description + "Typing 'i' next to ABgh");
|
|
},
|
|
},
|
|
{ test () {
|
|
synthesizeCompositionChange(
|
|
{ "composition":
|
|
{ "string": "GHI",
|
|
"clauses":
|
|
[
|
|
{ "length": 3, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
|
|
]
|
|
},
|
|
"caret": { "start": 3, "length": 0 }
|
|
});
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABGHIef", description + "Convert 'ghi' to 'GHI'");
|
|
},
|
|
},
|
|
{ test () {
|
|
// Commit composition
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
},
|
|
check () {
|
|
is(getValue(aEditor), "ABGHIef", description + "Commit 'GHI'");
|
|
},
|
|
},
|
|
];
|
|
|
|
function doReframe(aEvent)
|
|
{
|
|
aEvent.target.style.overflow =
|
|
aEvent.target.style.overflow != "hidden" ? "hidden" : "auto";
|
|
}
|
|
aEditor.focus();
|
|
aEditor.addEventListener(aEventType, doReframe);
|
|
|
|
for (const currentTest of tests) {
|
|
currentTest.test();
|
|
await waitForEventLoops(20);
|
|
currentTest.check();
|
|
await waitForTick();
|
|
}
|
|
|
|
await new Promise(resolve => {
|
|
aEditor.style.overflow = "auto";
|
|
aEditor.removeEventListener(aEventType, doReframe);
|
|
requestAnimationFrame(() => { SimpleTest.executeSoon(resolve); });
|
|
});
|
|
}
|
|
|
|
// TODO: Add "beforeinput" case.
|
|
input.value = "";
|
|
await runEditorReframeTest(input, window, "input");
|
|
input.value = "";
|
|
await runEditorReframeTest(input, window, "compositionupdate");
|
|
textarea.value = "";
|
|
await runEditorReframeTest(textarea, window, "input");
|
|
textarea.value = "";
|
|
await runEditorReframeTest(textarea, window, "compositionupdate");
|
|
contenteditable.innerHTML = "";
|
|
await runEditorReframeTest(contenteditable, windowOfContenteditable, "input");
|
|
contenteditable.innerHTML = "";
|
|
await runEditorReframeTest(contenteditable, windowOfContenteditable, "compositionupdate");
|
|
}
|
|
|
|
async function runIMEContentObserverTest()
|
|
{
|
|
let notifications = [];
|
|
let onReceiveNotifications = null;
|
|
function callback(aTIP, aNotification)
|
|
{
|
|
if (aNotification.type != "notify-end-input-transaction") {
|
|
notifications.push(aNotification);
|
|
}
|
|
switch (aNotification.type) {
|
|
case "request-to-commit":
|
|
aTIP.commitComposition();
|
|
break;
|
|
case "request-to-cancel":
|
|
aTIP.cancelComposition();
|
|
break;
|
|
}
|
|
if (onReceiveNotifications) {
|
|
let resolve = onReceiveNotifications;
|
|
onReceiveNotifications = null;
|
|
SimpleTest.executeSoon(() => {
|
|
resolve();
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function dumpUnexpectedNotifications(aDescription, aExpectedCount)
|
|
{
|
|
if (notifications.length <= aExpectedCount) {
|
|
return;
|
|
}
|
|
for (let i = aExpectedCount; i < notifications.length; i++) {
|
|
ok(false,
|
|
aDescription + " caused unexpected notification: " + notifications[i].type);
|
|
}
|
|
}
|
|
|
|
function promiseReceiveNotifications()
|
|
{
|
|
notifications = [];
|
|
return new Promise(resolve => {
|
|
onReceiveNotifications = resolve;
|
|
});
|
|
}
|
|
|
|
function flushNotifications()
|
|
{
|
|
return new Promise(resolve => {
|
|
// FYI: Dispatching non-op keyboard events causes forcibly flushing pending
|
|
// notifications.
|
|
synthesizeKey("KEY_Unidentified", { code: "" });
|
|
SimpleTest.executeSoon(()=>{
|
|
notifications = [];
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
function ensureToRemovePrecedingPositionChangeNotification(aDescription)
|
|
{
|
|
if (!notifications.length) {
|
|
return;
|
|
}
|
|
if (notifications[0].type != "notify-position-change") {
|
|
return;
|
|
}
|
|
// Sometimes, notify-position-change is notified first separately if
|
|
// the operation causes scroll or something. Tests can ignore this.
|
|
ok(true, "notify-position-change", aDescription + "Unnecessary notify-position-change occurred, ignoring it");
|
|
notifications.shift();
|
|
}
|
|
|
|
// Bug 1374057 - On ubuntu 16.04 there are notify-position-change events that are
|
|
// recorded after all the other events so we remove them through this function.
|
|
function ensureToRemovePostPositionChangeNotification(aDescription, expectedCount)
|
|
{
|
|
if (!notifications.length) {
|
|
return;
|
|
}
|
|
if (notifications.length <= expectedCount) {
|
|
return;
|
|
}
|
|
if (notifications[notifications.length-1].type != "notify-position-change") {
|
|
return;
|
|
}
|
|
ok(true, "notify-position-change", aDescription + "Unnecessary notify-position-change occurred, ignoring it");
|
|
notifications.pop();
|
|
}
|
|
|
|
function getNativeText(aXPText)
|
|
{
|
|
if (kLF == "\n") {
|
|
return aXPText;
|
|
}
|
|
return aXPText.replace(/\n/g, kLF);
|
|
}
|
|
|
|
function checkPositionChangeNotification(aNotification, aDescription)
|
|
{
|
|
is(!aNotification || aNotification.type, "notify-position-change",
|
|
aDescription + " should cause position change notification");
|
|
}
|
|
|
|
function checkSelectionChangeNotification(aNotification, aDescription, aExpected)
|
|
{
|
|
is(!aNotification || aNotification.type, "notify-selection-change",
|
|
aDescription + " should cause selection change notification");
|
|
if (!aNotification || (aNotification.type != "notify-selection-change")) {
|
|
return;
|
|
}
|
|
is(aNotification.offset, aExpected.offset,
|
|
aDescription + " should cause selection change notification whose offset is " + aExpected.offset);
|
|
is(aNotification.text, aExpected.text,
|
|
aDescription + " should cause selection change notification whose text is '" + aExpected.text + "'");
|
|
is(aNotification.collapsed, !aExpected.text.length,
|
|
aDescription + " should cause selection change notification whose collapsed is " + (!aExpected.text.length));
|
|
is(aNotification.length, aExpected.text.length,
|
|
aDescription + " should cause selection change notification whose length is " + aExpected.text.length);
|
|
is(aNotification.reversed, aExpected.reversed || false,
|
|
aDescription + " should cause selection change notification whose reversed is " + (aExpected.reversed || false));
|
|
is(aNotification.writingMode, aExpected.writingMode || "horizontal-tb",
|
|
aDescription + " should cause selection change notification whose writingMode is '" + (aExpected.writingMode || "horizontal-tb"));
|
|
}
|
|
|
|
function checkTextChangeNotification(aNotification, aDescription, aExpected)
|
|
{
|
|
is(!aNotification || aNotification.type, "notify-text-change",
|
|
aDescription + " should cause text change notification");
|
|
if (!aNotification || aNotification.type != "notify-text-change") {
|
|
return;
|
|
}
|
|
is(aNotification.offset, aExpected.offset,
|
|
aDescription + " should cause text change notification whose offset is " + aExpected.offset);
|
|
is(aNotification.removedLength, aExpected.removedLength,
|
|
aDescription + " should cause text change notification whose removedLength is " + aExpected.removedLength);
|
|
is(aNotification.addedLength, aExpected.addedLength,
|
|
aDescription + " should cause text change notification whose addedLength is " + aExpected.addedLength);
|
|
}
|
|
|
|
async function testWithPlaintextEditor(aDescription, aElement, aTestLineBreaker)
|
|
{
|
|
aElement.value = "";
|
|
aElement.blur();
|
|
let doc = aElement.ownerDocument;
|
|
let win = doc.defaultView;
|
|
aElement.focus();
|
|
await flushNotifications();
|
|
|
|
// "a[]"
|
|
let description = aDescription + "typing 'a'";
|
|
let waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("a", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0, removedLength: 0, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 1, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "ab[]"
|
|
description = aDescription + "typing 'b'";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("b", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1, removedLength: 0, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "abc[]"
|
|
description = aDescription + "typing 'c'";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("c", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2, removedLength: 0, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 3, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "ab[c]"
|
|
description = aDescription + "selecting 'c' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 2, text: "c", reversed: true });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "a[bc]"
|
|
description = aDescription + "selecting 'bc' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 1, text: "bc", reversed: true });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[abc]"
|
|
description = aDescription + "selecting 'bc' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0, text: "abc", reversed: true });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[]abc"
|
|
description = aDescription + "collapsing selection to the left-most with pressing ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0, text: "" });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[a]bc"
|
|
description = aDescription + "selecting 'a' with pressing Shift+ArrowRight";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowRight", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0, text: "a" });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[ab]c"
|
|
description = aDescription + "selecting 'ab' with pressing Shift+ArrowRight";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowRight", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0, text: "ab" });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[]c"
|
|
description = aDescription + "deleting 'ab' with pressing Delete";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0, removedLength: 2, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "[]"
|
|
description = aDescription + "deleting following 'c' with pressing Delete";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0, removedLength: 1, addedLength: 0 });
|
|
checkPositionChangeNotification(notifications[1], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 2);
|
|
dumpUnexpectedNotifications(description, 2);
|
|
|
|
// "abc[]"
|
|
synthesizeKey("a", {}, win, callback);
|
|
synthesizeKey("b", {}, win, callback);
|
|
synthesizeKey("c", {}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "ab[]"
|
|
description = aDescription + "deleting 'c' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2, removedLength: 1, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "[ab]"
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "[]"
|
|
description = aDescription + "deleting 'ab' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0, removedLength: 2, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "abcd[]"
|
|
synthesizeKey("a", {}, win, callback);
|
|
synthesizeKey("b", {}, win, callback);
|
|
synthesizeKey("c", {}, win, callback);
|
|
synthesizeKey("d", {}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "a[bc]d"
|
|
synthesizeKey("KEY_ArrowLeft", {}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "a[]d"
|
|
description = aDescription + "deleting 'bc' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1, removedLength: 2, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 1, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "a[bc]d"
|
|
synthesizeKey("b", {}, win, callback);
|
|
synthesizeKey("c", {}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "aB[]d"
|
|
description = aDescription + "replacing 'bc' with 'B' with pressing Shift+B";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("B", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1, removedLength: 2, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
if (!aTestLineBreaker) {
|
|
return;
|
|
}
|
|
|
|
// "aB\n[]d"
|
|
description = aDescription + "inserting a line break after 'B' with pressing Enter";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Enter", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2, removedLength: 0, addedLength: kLFLen });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("aB\n").length, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "aB[]d"
|
|
description = aDescription + "removing a line break after 'B' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2, removedLength: kLFLen, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "a[B]d"
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "a\n[]d"
|
|
description = aDescription + "replacing 'B' with a line break with pressing Enter";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Enter", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1, removedLength: 1, addedLength: kLFLen });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("a\n").length, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "a[\n]d"
|
|
description = aDescription + "selecting '\n' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 1, text: kLF, reversed: true });
|
|
ensureToRemovePostPositionChangeNotification(description, 1);
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "a[]d"
|
|
description = aDescription + "removing selected '\\n' with pressing Delete";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1, removedLength: kLFLen, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 1, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// ab\ncd\nef\ngh\n[]
|
|
description = aDescription + "setting the value property to 'ab\ncd\nef\ngh\n'";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
aElement.value = "ab\ncd\nef\ngh\n";
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0, removedLength: 2, addedLength: getNativeText("ab\ncd\nef\ngh\n").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("ab\ncd\nef\ngh\n").length, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// []
|
|
description = aDescription + "setting the value property to ''";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
aElement.value = "";
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0, removedLength: getNativeText("ab\ncd\nef\ngh\n").length, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
ensureToRemovePostPositionChangeNotification(description, 3);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
}
|
|
|
|
async function testWithHTMLEditor(aDescription, aElement, aDefaultParagraphSeparator)
|
|
{
|
|
let doc = aElement.ownerDocument;
|
|
let win = doc.defaultView;
|
|
let sel = doc.getSelection();
|
|
let inDesignMode = doc.designMode == "on";
|
|
let offsetAtStart = 0;
|
|
let offsetAtContainer = 0;
|
|
let isDefaultParagraphSeparatorBlock = aDefaultParagraphSeparator != "br";
|
|
doc.execCommand("defaultParagraphSeparator", false, aDefaultParagraphSeparator);
|
|
|
|
// "[]", "<p>[]</p>" or "<div>[]</div>"
|
|
switch (aDefaultParagraphSeparator) {
|
|
case "br":
|
|
aElement.innerHTML = "";
|
|
break;
|
|
case "p":
|
|
case "div":
|
|
aElement.innerHTML = "<" + aDefaultParagraphSeparator + "></" + aDefaultParagraphSeparator + ">";
|
|
sel.collapse(aElement.firstChild, 0);
|
|
offsetAtContainer = offsetAtStart + kLFLen;
|
|
break;
|
|
default:
|
|
ok(false, aDescription + "aDefaultParagraphSeparator is illegal value");
|
|
await flushNotifications();
|
|
return;
|
|
}
|
|
|
|
if (inDesignMode) {
|
|
win.focus();
|
|
} else {
|
|
aElement.focus();
|
|
}
|
|
await flushNotifications();
|
|
|
|
// "a[]"
|
|
let description = aDescription + "typing 'a'";
|
|
let waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("a", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: 0, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 1 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "ab[]"
|
|
description = aDescription + "typing 'b'";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("b", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, removedLength: 0, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "abc[]"
|
|
description = aDescription + "typing 'c'";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("c", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2 + offsetAtContainer, removedLength: 0, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 3 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "ab[c]"
|
|
description = aDescription + "selecting 'c' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 2 + offsetAtContainer, text: "c", reversed: true });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "a[bc]"
|
|
description = aDescription + "selecting 'bc' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, text: "bc", reversed: true });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[abc]"
|
|
description = aDescription + "selecting 'bc' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, text: "abc", reversed: true });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[]abc"
|
|
description = aDescription + "collapsing selection to the left-most with pressing ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, text: "" });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[a]bc"
|
|
description = aDescription + "selecting 'a' with pressing Shift+ArrowRight";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowRight", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, text: "a" });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[ab]c"
|
|
description = aDescription + "selecting 'ab' with pressing Shift+ArrowRight";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowRight", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, text: "ab" });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "[]c"
|
|
description = aDescription + "deleting 'ab' with pressing Delete";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: 2, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "[]"
|
|
description = aDescription + "deleting following 'c' with pressing Delete";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: 1, addedLength: 0 });
|
|
checkPositionChangeNotification(notifications[1], description);
|
|
dumpUnexpectedNotifications(description, 2);
|
|
|
|
// "abc[]"
|
|
synthesizeKey("a", {}, win, callback);
|
|
synthesizeKey("b", {}, win, callback);
|
|
synthesizeKey("c", {}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "ab[]"
|
|
description = aDescription + "deleting 'c' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2 + offsetAtContainer, removedLength: 1, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "[ab]"
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "[]<br>"
|
|
description = aDescription + "deleting 'ab' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: 2, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "abcd[]"
|
|
synthesizeKey("a", {}, win, callback);
|
|
synthesizeKey("b", {}, win, callback);
|
|
synthesizeKey("c", {}, win, callback);
|
|
synthesizeKey("d", {}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "a[bc]d"
|
|
synthesizeKey("KEY_ArrowLeft", {}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "a[]d"
|
|
description = aDescription + "deleting 'bc' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, removedLength: 2, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 1 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "a[bc]d"
|
|
synthesizeKey("b", {}, win, callback);
|
|
synthesizeKey("c", {}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "aB[]d"
|
|
description = aDescription + "replacing 'bc' with 'B' with pressing Shift+B";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("B", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, removedLength: 2, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "aB<br>[]d" or "<block>aB</block><block>[]d</block>"
|
|
description = aDescription + "inserting a line break after 'B' with pressing Enter";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Enter", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
if (isDefaultParagraphSeparatorBlock) {
|
|
// Pressing Enter when aB[]d, then, it becomes <block>aB</block><block>[]d</block>.
|
|
// All text is replaced with 2 paragraphs.
|
|
// <block>aB[]d</block> (removed aBd, added \naBd)
|
|
// <block>aB and d</block> (removed d and added d)
|
|
// <block>aB<br>[]d</block> (added \n)
|
|
// <block>aB<br></block><block>[]d</block> (removed \nd and added \nd)
|
|
// <block>aB</block><block>[]d</block> (removed \n)
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "aB".length,
|
|
removedLength: getNativeText("d").length,
|
|
addedLength: getNativeText("\nd").length,
|
|
});
|
|
} else {
|
|
// Inserting <br> causes removing "d" and inserting "<br>d"
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "aB".length,
|
|
removedLength: "d".length,
|
|
addedLength: getNativeText("\nd").length,
|
|
});
|
|
}
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("aB\n").length + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "aB[]d" or "<block>aB[]d</block>
|
|
description = aDescription + "removing a line break after 'B' with pressing Backspace";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Backspace", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
if (isDefaultParagraphSeparatorBlock) {
|
|
// Joining two blocks causes removing "</block><block>d</block>" and inserting "d</block>"
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + getNativeText("aB".length),
|
|
removedLength: getNativeText("\nd").length,
|
|
addedLength: "d".length,
|
|
});
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "aB".length,
|
|
removedLength: kLFLen,
|
|
addedLength: 0,
|
|
});
|
|
is(notifications.length, 3, description + " should cause 3 notifications");
|
|
is(notifications[1] && notifications[1].type, "notify-selection-change", description + " should cause selection change notification");
|
|
is(notifications[2] && notifications[2].type, "notify-position-change", description + " should cause position change notification");
|
|
}
|
|
|
|
// "a[B]d"
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await flushNotifications();
|
|
|
|
// "a<br>[]d" or "<block>a</block><block>[]d</block>"
|
|
description = aDescription + "replacing 'B' with a line break with pressing Enter";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Enter", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
if (isDefaultParagraphSeparatorBlock) {
|
|
// a[]d (removed B)
|
|
// <block>a[]d</block> (removed ad and added \nad)
|
|
// <block>a and d</block> (removed d and added d)
|
|
// <block>a<br>d</block> (added \n)
|
|
// <block>a<br></block><block>d</block> (removed d and added \nd)
|
|
// <block>a</block><block>d</block> (removed `\n)
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "a".length,
|
|
removedLength: getNativeText("Bd").length,
|
|
addedLength: getNativeText("\nd").length,
|
|
});
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "a".length,
|
|
removedLength: "B".length,
|
|
addedLength: kLFLen,
|
|
});
|
|
}
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("a\n").length + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "a[<br>]d" or "<block>a[</block><block>]d</block>"
|
|
description = aDescription + "selecting '\\n' with pressing Shift+ArrowLeft";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_ArrowLeft", {shiftKey: true}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkSelectionChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, text: kLF, reversed: true });
|
|
dumpUnexpectedNotifications(description, 1);
|
|
|
|
// "a[]d" or "<block>a[]d</block>"
|
|
description = aDescription + "removing selected '\\n' with pressing Delete";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
if (isDefaultParagraphSeparatorBlock) {
|
|
// Joining the blocks causes removing "</block><block>d</block>" and inserting "d</block>".
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "a".length,
|
|
removedLength: getNativeText("\nd").length,
|
|
addedLength: getNativeText("d").length,
|
|
});
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, {
|
|
offset: offsetAtContainer + "a".length,
|
|
removedLength: kLFLen,
|
|
addedLength: 0,
|
|
});
|
|
}
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 1 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5</div>"
|
|
description = aDescription + "inserting HTML which has nested block elements";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5</div>";
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
// There is <br> after the end of the line. Therefore, removed length includes a line breaker length.
|
|
if (isDefaultParagraphSeparatorBlock) {
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer - kLFLen, removedLength: getNativeText("\nad").length, addedLength: getNativeText("\n1\n2\n345").length });
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: getNativeText("ad").length, addedLength: getNativeText("\n1\n2\n345").length });
|
|
}
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1[<div>2<div>3</div>4</div>]5</div>" and removing selection
|
|
sel.setBaseAndExtent(aElement.firstChild.firstChild, 1, aElement.firstChild.childNodes.item(2), 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>15</div>", description + " should remove '<div>2<div>3</div>4</div>'");
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1").length + offsetAtStart, removedLength: getNativeText("\n2\n34").length, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1[<div>2<div>3</div>]4</div>5</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.firstChild, 1, aElement.firstChild.childNodes.item(1).childNodes.item(2), 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (partially #1) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>145</div>", description + " should remove '<div>2<div>3</div></div>'");
|
|
// It causes removing '<div>2<div>3</div>4</div>' and inserting '4'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1").length + offsetAtStart, removedLength: getNativeText("\n2\n34").length, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1[<div>2<div>]3</div>4</div>5</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.firstChild, 1, aElement.firstChild.childNodes.item(1).childNodes.item(1).firstChild, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (partially #2) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>13<div>4</div>5</div>", description + " should remove '<div>2</div>'");
|
|
checkTextChangeNotification(
|
|
notifications[0],
|
|
description,
|
|
{
|
|
offset: kLFLen + offsetAtStart + "1".length,
|
|
// It causes removing '<div>2<div>3</div></div>' and inserting '13<div>'.
|
|
removedLength: getNativeText("\n2\n3").length,
|
|
addedLength: getNativeText("3\n").length
|
|
}
|
|
);
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1<div>2<div>3[</div>4</div>]5</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.childNodes.item(1).childNodes.item(1).firstChild, 1, aElement.firstChild.childNodes.item(2), 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (partially #3) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>1<div>2<div>35</div></div></div>", description + " should remove '4'");
|
|
// It causes removing '45' and inserting '5'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1\n2\n3").length + offsetAtStart, removedLength: 2, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1\n2\n3").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5<div>6<div>7</div>8</div>9</div>"
|
|
description = aDescription + "inserting HTML which has a pair of nested block elements";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5<div>6<div>7</div>8</div>9</div>";
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtStart, removedLength: getNativeText("\n1\n2\n35").length, addedLength: getNativeText("\n1\n2\n345\n6\n789").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 0 + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1<div>2<div>3[</div>4</div>5<div>6<div>]7</div>8</div>9</div>" and removing selection
|
|
sel.setBaseAndExtent(aElement.firstChild.childNodes.item(1).childNodes.item(1).firstChild, 1, aElement.firstChild.childNodes.item(3).childNodes.item(1).firstChild, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (between same level descendants) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>1<div>2<div>37</div></div><div>8</div>9</div>", description + " should remove '456<div>7'");
|
|
// It causes removing '<div>3</div>4</div>5<div>6<div>7</div>' and inserting '<div>37</div><div>'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1\n2").length + offsetAtStart, removedLength: getNativeText("\n345\n6\n7").length, addedLength: getNativeText("\n37\n").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1\n2\n3").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1<div>2[<div>3</div>4</div>5<div>6<div>]7</div>8</div>9</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5<div>6<div>7</div>8</div>9</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.childNodes.item(1).firstChild, 1, aElement.firstChild.childNodes.item(3).childNodes.item(1).firstChild, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (between different level descendants #1) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>1<div>27</div><div>8</div>9</div>", description + " should remove '<div>2<div>3</div>4</div>5<div>6<div>7</div>'");
|
|
// It causes removing '<div>2<div>3</div>4</div>5<div>6<div>7</div>' and inserting '<div>27</div>'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1").length + offsetAtStart, removedLength: getNativeText("\n2\n345\n6\n7").length, addedLength: getNativeText("\n27\n").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1\n2").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1<div>2[<div>3</div>4</div>5<div>6<div>7</div>8</div>]9</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5<div>6<div>7</div>8</div>9</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.childNodes.item(1).firstChild, 1, aElement.firstChild.childNodes.item(4), 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (between different level descendants #2) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>1<div>29</div></div>", description + " should remove '<div>3</div>4</div>5<div>6<div>7</div>8</div>'");
|
|
// It causes removing '<div>3</div>4</div>5</div>6<div>7</div>8</div>9' and inserting '9</div>'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1\n2").length + offsetAtStart, removedLength: getNativeText("\n345\n6\n789").length, addedLength: 1 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1\n2").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1<div>2<div>3[</div>4</div>5<div>]6<div>7</div>8</div>9</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5<div>6<div>7</div>8</div>9</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.childNodes.item(1).childNodes.item(1).firstChild, 1, aElement.firstChild.childNodes.item(3).firstChild, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (between different level descendants #3) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>1<div>2<div>36<div>7</div>8</div></div>9</div>", description + " should remove '<div>36<div>7</div>8</div>'");
|
|
// It causes removing '<div>3</div>4</div>5<div>6<div>7</div>8</div>' and inserting '<div>36<div>7</div>8</div>'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1\n2").length + offsetAtStart, removedLength: getNativeText("\n345\n6\n78").length, addedLength: getNativeText("\n36\n78").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1\n2\n3").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<div>1<div>2<div>3[</div>4</div>5<div>6<div>7</div>8</div>]9</div>" and removing selection
|
|
aElement.innerHTML = "<div>1<div>2<div>3</div>4</div>5<div>6<div>7</div>8</div>9</div>";
|
|
sel.setBaseAndExtent(aElement.firstChild.childNodes.item(1).childNodes.item(1).firstChild, 1, aElement.firstChild.childNodes.item(4), 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting child nodes (between different level descendants #4) with pressing Delete key";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Delete", {}, win, callback);
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<div>1<div>2<div>39</div></div></div>", description + " should remove '</div>4</div>5<div>6<div>7</div>8</div>'");
|
|
// It causes removing '</div>4</div>5<div>6<div>7</div>8</div>' and inserting '<div>36<div>7</div>8</div>'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\n1\n2\n3").length + offsetAtStart, removedLength: getNativeText("45\n6\n789").length, addedLength: getNativeText("9").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\n1\n2\n3").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<p>abc</p><p><br></p><p>{<br>}</p>" and removing second paragraph with DOM API
|
|
aElement.innerHTML = "<p>abc</p><p><br></p><p><br></p>";
|
|
sel.collapse(aElement.firstChild.nextSibling.nextSibling, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting previous paragraph with DOM API";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Unidentified", { code: "" }, win, callback); // For setting the callback to recode notifications
|
|
aElement.firstChild.nextSibling.remove();
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<p>abc</p><p><br></p>", description + " the second paragraph should've been removed");
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\nabc").length + offsetAtStart, removedLength: getNativeText("\n\n").length, addedLength: 0 });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("\nabc\n").length + offsetAtStart, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
|
|
// "<p>abc</p><p>{<br>}</p><p><br></p>" and removing last paragraph with DOM API
|
|
aElement.innerHTML = "<p>abc</p><p><br></p><p><br></p>";
|
|
sel.collapse(aElement.firstChild.nextSibling, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "deleting next paragraph with DOM API";
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Unidentified", { code: "" }, win, callback); // For setting the callback to recode notifications
|
|
aElement.firstChild.nextSibling.nextSibling.remove();
|
|
await waitNotifications;
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(aElement.innerHTML, "<p>abc</p><p><br></p>", description + " the last paragraph should've been removed");
|
|
checkTextChangeNotification(notifications[0], description, { offset: getNativeText("\nabc\n\n").length + offsetAtStart, removedLength: getNativeText("\n\n").length, addedLength: 0 });
|
|
checkPositionChangeNotification(notifications[1], description);
|
|
dumpUnexpectedNotifications(description, 2);
|
|
|
|
await (async function test_insert_multiple_consecutive_siblings() {
|
|
aElement.innerHTML = "<p><br></p>";
|
|
const p = aElement.querySelector("p");
|
|
sel.collapse(p, 0);
|
|
await flushNotifications();
|
|
description = aDescription + "Insert multiple consecutive siblings from DOMNodeInserted event listener";
|
|
aElement.addEventListener("DOMNodeInserted", () => {
|
|
const span1 = doc.createElement("span");
|
|
span1.textContent = "a";
|
|
p.insertBefore(span1, p.firstChild);
|
|
const span2 = doc.createElement("span");
|
|
span2.textContent = "b";
|
|
p.insertBefore(span2, p.firstChild);
|
|
const span3 = doc.createElement("span");
|
|
span3.textContent = "c";
|
|
p.insertBefore(span3, p.firstChild);
|
|
const span4 = doc.createElement("span");
|
|
span4.textContent = "d";
|
|
p.insertBefore(span4, p.firstChild);
|
|
// Now, span4 should be cached as first added node and <br> should be
|
|
// cached as the last added node in IMEContentObserver.
|
|
// span2 is not adjacent sibling of the caching range boundaries.
|
|
// Therefore, this requires to compute index of span2 in the <p>.
|
|
// We want to check the path.
|
|
const span5 = doc.createElement("span");
|
|
span5.textContent = "e";
|
|
span2.insertBefore(span5, span2.firstChild);
|
|
}, {once: true});
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Enter", { shiftKey: true }, win, callback); // For setting the callback to recode notifications
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(
|
|
aElement.innerHTML,
|
|
"<p><span>d</span><span>c</span><span><span>e</span>b</span><span>a</span><br><br></p>",
|
|
description + " <span> elements should be inserted before the new <br>"
|
|
);
|
|
checkTextChangeNotification(
|
|
notifications[0],
|
|
description,
|
|
{
|
|
offset: getNativeText("\n").length + offsetAtStart,
|
|
removedLength: 0,
|
|
addedLength: getNativeText("dceba\n").length,
|
|
}
|
|
);
|
|
})();
|
|
|
|
await (async function test_insert_multiple_indirect_siblings() {
|
|
aElement.innerHTML = "<p><span>a</span><br><br></p>";
|
|
const p = aElement.querySelector("p");
|
|
sel.collapse(p, 2);
|
|
await flushNotifications();
|
|
description = aDescription + "Insert multiple indirect siblings from DOMNodeInserted event listener";
|
|
aElement.addEventListener("DOMNodeInserted", () => {
|
|
const span0 = p.querySelector("span");
|
|
const newBR = p.querySelector("br + br");
|
|
const span1 = doc.createElement("span");
|
|
span1.textContent = "b";
|
|
p.insertBefore(span1, newBR);
|
|
const span2 = doc.createElement("span");
|
|
span2.textContent = "c";
|
|
p.insertBefore(span2, newBR);
|
|
// Now, span1 should be cached as first added node and <br> should be
|
|
// cached as the last added node in IMEContentObserver.
|
|
// Then, inserting new node before the first <br> which is before the
|
|
// span1 (first added node), it should be
|
|
const span3 = doc.createElement("span");
|
|
span3.textContent = "d";
|
|
span0.insertBefore(span3, span0.firstChild);
|
|
}, {once: true});
|
|
waitNotifications = promiseReceiveNotifications();
|
|
synthesizeKey("KEY_Enter", { shiftKey: true }, win, callback); // For setting the callback to recode notifications
|
|
ensureToRemovePrecedingPositionChangeNotification();
|
|
is(
|
|
aElement.innerHTML,
|
|
"<p><span><span>d</span>a</span><br><span>b</span><span>c</span><br><br></p>",
|
|
description + " <span> elements should be inserted before the new <br>"
|
|
);
|
|
// However, the text changes should be merged into one notification.
|
|
checkTextChangeNotification(
|
|
notifications[0],
|
|
description,
|
|
{
|
|
offset: getNativeText("\n").length + offsetAtStart,
|
|
removedLength: "a\n".length,
|
|
addedLength: getNativeText("da\nbc\n").length,
|
|
}
|
|
);
|
|
})();
|
|
}
|
|
|
|
await testWithPlaintextEditor("runIMEContentObserverTest with input element: ", input, false);
|
|
await testWithPlaintextEditor("runIMEContentObserverTest with textarea element: ", textarea, true);
|
|
await testWithHTMLEditor("runIMEContentObserverTest with contenteditable (defaultParagraphSeparator is br): ", contenteditable, "br");
|
|
await testWithHTMLEditor("runIMEContentObserverTest with contenteditable (defaultParagraphSeparator is p): ", contenteditable, "p");
|
|
await testWithHTMLEditor("runIMEContentObserverTest with contenteditable (defaultParagraphSeparator is div): ", contenteditable, "div");
|
|
// XXX Due to the difference of HTML editor behavior between designMode and contenteditable,
|
|
// testWithHTMLEditor() gets some unexpected behavior. However, IMEContentObserver is
|
|
// not depend on editor's detail. So, we should investigate this issue later. It's not
|
|
// so important for now.
|
|
// await testWithHTMLEditor("runIMEContentObserverTest in designMode (defaultParagraphSeparator is br): ", iframe2.contentDocument.body, "br");
|
|
// await testWithHTMLEditor("runIMEContentObserverTest in designMode (defaultParagraphSeparator is p): ", iframe2.contentDocument.body, "p");
|
|
// await testWithHTMLEditor("runIMEContentObserverTest in designMode (defaultParagraphSeparator is div): ", iframe2.contentDocument.body, "div");
|
|
}
|
|
|
|
async function runPasswordMaskDelayTest() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["editor.password.mask_delay", 600],
|
|
["editor.password.testing.mask_delay", true],
|
|
],
|
|
});
|
|
|
|
let iframe5 = document.getElementById("iframe5");
|
|
let iframe6 = document.getElementById("iframe6");
|
|
let inputWindow = iframe5.contentWindow;
|
|
let passwordWindow = iframe6.contentWindow;
|
|
|
|
let inputElement = iframe5.contentDocument.getElementById("input");
|
|
let passwordElement = iframe6.contentDocument.getElementById("password");
|
|
|
|
const kMask = passwordElement.editor.passwordMask;
|
|
|
|
function promiseAllPasswordMasked() {
|
|
return new Promise(resolve => {
|
|
passwordElement.addEventListener("MozLastInputMasked", resolve, {once: true});
|
|
});
|
|
}
|
|
|
|
function checkSnapshots(aResult, aReference, aMatch, aDescription) {
|
|
let [correct, data1, data2] = compareSnapshots(aResult, aReference, true);
|
|
is(correct, aMatch, `${aDescription}\nREFTEST IMAGE 1 (TEST): ${data1}\nREFTEST IMAGE 2 (REFERENCE): ${data2}`);
|
|
}
|
|
|
|
// First character input
|
|
passwordElement.value = "";
|
|
passwordElement.focus();
|
|
let waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeKey("a");
|
|
let unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
let maskedResult = await snapshotWindow(passwordWindow, true);
|
|
|
|
inputElement.value = "a";
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(1, 1);
|
|
let unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
inputElement.value = kMask;
|
|
inputElement.setSelectionRange(1, 1);
|
|
let maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): first inputted character should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): first inputted character should be masked after a while");
|
|
|
|
// Second character input
|
|
passwordElement.value = "a";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(1, 1);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeKey("b");
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
|
|
inputElement.value = `${kMask}b`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(2, 2);
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
inputElement.value = `${kMask}${kMask}`;
|
|
inputElement.setSelectionRange(2, 2);
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): second inputted character should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): second inputted character should be masked after a while");
|
|
|
|
// Typing new character should mask the previous unmasked characters
|
|
passwordElement.value = "ab";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(2, 2);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeKey("c");
|
|
synthesizeKey("d");
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
|
|
inputElement.value = `${kMask}${kMask}${kMask}d`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(4, 4);
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
inputElement.value = `${kMask}${kMask}${kMask}${kMask}`;
|
|
inputElement.setSelectionRange(4, 4);
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): forth character input should mask the third character");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): forth inputted character should be masked after a while");
|
|
|
|
// Typing middle of password should unmask the last input character
|
|
passwordElement.value = "abcd";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(2, 2);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeKey("e");
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
|
|
inputElement.value = `${kMask}${kMask}e${kMask}${kMask}`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(3, 3);
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
inputElement.value = `${kMask}${kMask}${kMask}${kMask}${kMask}`;
|
|
inputElement.setSelectionRange(3, 3);
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): inserted character should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): inserted character should be masked after a while");
|
|
|
|
// Composition string should be unmasked for a while, and shouldn't be committed at masking
|
|
passwordElement.value = "ab";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(1, 1);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "c",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
is(getEditor(passwordElement).composing, true,
|
|
"runPasswordMaskDelayTest(): composition shouldn't be commited at masking the composing string #1");
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
inputElement.value = `${kMask}${kMask}`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(1, 1);
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "c",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: kMask,
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): composing character should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): composing character should be masked after a while");
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
// Updating composition string should unmask the composition string for a while
|
|
passwordElement.value = "ab";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(1, 1);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "c",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
await waitForMaskingLastInput;
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "d",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
is(getEditor(passwordElement).composing, true,
|
|
"runPasswordMaskDelayTest(): composition shouldn't be commited at masking the composing string #2");
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
inputElement.value = `${kMask}${kMask}`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(1, 1);
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "d",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: kMask,
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): updated composing character should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): updated composing character should be masked after a while");
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
// Composing multi-characters should be unmasked for a while.
|
|
passwordElement.value = "ab";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(1, 1);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "c",
|
|
clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 1, length: 0 },
|
|
});
|
|
await waitForMaskingLastInput;
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "cd",
|
|
clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 2, length: 0 },
|
|
});
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
is(getEditor(passwordElement).composing, true,
|
|
"runPasswordMaskDelayTest(): composition shouldn't be commited at masking the composing string #3");
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
inputElement.value = `${kMask}${kMask}`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(1, 1);
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "cd",
|
|
clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 2, length: 0 },
|
|
});
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: `${kMask}${kMask}`,
|
|
clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 2, length: 0 },
|
|
});
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): all of composing string should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): all of composing string should be masked after a while");
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
|
|
// Committing composition should make the commit string unmasked.
|
|
passwordElement.value = "ab";
|
|
passwordElement.focus();
|
|
passwordElement.setSelectionRange(1, 1);
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeCompositionChange(
|
|
{ composition:
|
|
{ string: "cd",
|
|
clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }],
|
|
},
|
|
caret: { start: 2, length: 0 },
|
|
});
|
|
await waitForMaskingLastInput;
|
|
waitForMaskingLastInput = promiseAllPasswordMasked();
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
|
|
unmaskedResult = await snapshotWindow(passwordWindow, true);
|
|
await waitForMaskingLastInput;
|
|
maskedResult = await snapshotWindow(passwordWindow, true);
|
|
|
|
inputElement.value = `${kMask}cd${kMask}`;
|
|
inputElement.focus();
|
|
inputElement.setSelectionRange(3, 3);
|
|
unmaskedReference = await snapshotWindow(inputWindow, true);
|
|
inputElement.value = `${kMask}${kMask}${kMask}${kMask}`;
|
|
inputElement.setSelectionRange(3, 3);
|
|
maskedReference = await snapshotWindow(inputWindow, true);
|
|
checkSnapshots(unmaskedResult, unmaskedReference, true,
|
|
"runPasswordMaskDelayTest(): committed string should be unmasked for a while");
|
|
checkSnapshots(maskedResult, maskedReference, true,
|
|
"runPasswordMaskDelayTest(): committed string should be masked after a while");
|
|
}
|
|
|
|
async function runInputModeTest()
|
|
{
|
|
let result = [];
|
|
|
|
function handler(aEvent)
|
|
{
|
|
result.push(aEvent);
|
|
}
|
|
|
|
textarea.inputMode = "text";
|
|
textarea.value = "";
|
|
textarea.focus();
|
|
|
|
textarea.addEventListener("compositionupdate", handler, true);
|
|
textarea.addEventListener("compositionend", handler, true);
|
|
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a ", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
|
|
is(result[0].type, "compositionupdate", "Set initial composition for inputmode test");
|
|
result = [];
|
|
|
|
textarea.inputMode = "tel";
|
|
is(result.length, 0, "No compositonend event even if inputmode is updated");
|
|
|
|
// Clean up
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
textarea.inputMode = "";
|
|
textarea.value = "";
|
|
textarea.removeEventListener("compositionupdate", handler, true);
|
|
textarea.removeEventListener("compositionend", handler, true);
|
|
}
|
|
|
|
function runPaddingLineBreakTest() {
|
|
const selection = windowOfContenteditable.getSelection();
|
|
contenteditable.focus();
|
|
contenteditable.innerHTML = "<p><br></p>";
|
|
selection.collapse(contenteditable.querySelector("p"), 0);
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p>a</p>",
|
|
"Padding <br> for empty paragraph should be removed when composition string does not end with collapsible white-space"
|
|
);
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
synthesizeKey("z", {accelKey: true});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p><br></p>",
|
|
"Undo should restore the padding <br> for the empty paragraph too"
|
|
);
|
|
|
|
contenteditable.innerHTML = "<p><br></p>";
|
|
selection.collapse(contenteditable.querySelector("p"), 0);
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a ", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p>a <br></p>",
|
|
"Padding <br> should be inserted after composition string when it ends with a collapsible white-space"
|
|
);
|
|
synthesizeCompositionChange({
|
|
composition: {string: "a b", clauses: [{length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p>a b</p>",
|
|
"Padding <br> should be removed again when the last character becomes visible character"
|
|
);
|
|
synthesizeComposition({ type: "compositioncommitasis" });
|
|
synthesizeKey("z", {accelKey: true});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p><br></p>",
|
|
"Undo should restore the padding <br> for the empty paragraph with canceling the composition"
|
|
);
|
|
|
|
contenteditable.innerHTML = "<p>abc <br></p>";
|
|
selection.collapse(contenteditable.querySelector("p").firstChild, "abc ".length);
|
|
synthesizeCompositionChange({
|
|
composition: {string: "d", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
|
|
});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p>abc d</p>",
|
|
`The padding <br> should be deleted when the composition string, "d", is inserted`
|
|
);
|
|
synthesizeComposition({ type: "compositioncommit", data: "" });
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p>abc <br></p>",
|
|
"A padding <br> should be inserted if the composition string becomes empty and the previous character is a collapsible ASCII white-space"
|
|
);
|
|
synthesizeKey("z", {accelKey: true});
|
|
is(
|
|
contenteditable.innerHTML,
|
|
"<p>abc <br></p>",
|
|
"Undo should not delete the padding <br>"
|
|
);
|
|
|
|
contenteditable.innerHTML = "";
|
|
}
|
|
|
|
async function runTest()
|
|
{
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
["dom.events.textevent.enabled", true],
|
|
["test.ime_content_observer.assert_invalid_cache", true],
|
|
["dom.mutation_events.enabled", true],
|
|
],
|
|
});
|
|
|
|
window.addEventListener("unload", window.arguments[0].SimpleTest.finish, {once: true, capture: true});
|
|
|
|
contenteditable = document.getElementById("iframe4").contentDocument.getElementById("contenteditable");
|
|
windowOfContenteditable = document.getElementById("iframe4").contentWindow;
|
|
textareaInFrame = iframe.contentDocument.getElementById("textarea");
|
|
|
|
contenteditableBySpan = document.getElementById("iframe7").contentDocument.getElementById("contenteditable");
|
|
windowOfContenteditableBySpan = document.getElementById("iframe7").contentWindow;
|
|
|
|
await runIMEContentObserverTest();
|
|
await runEditorReframeTests();
|
|
await runAsyncForceCommitTest();
|
|
await runRemoveContentTest();
|
|
await runPanelTest();
|
|
await runPasswordMaskDelayTest();
|
|
await runBug1584901Test();
|
|
await runInputModeTest();
|
|
await runCompositionTest();
|
|
await runCompositionCommitTest();
|
|
await runSetSelectionEventTest();
|
|
|
|
runUndoRedoTest();
|
|
runCompositionCommitAsIsTest();
|
|
runCompositionEventTest();
|
|
runCompositionTestWhoseTextNodeModified();
|
|
runQueryTextRectInContentEditableTest();
|
|
runCharAtPointTest(textarea, "textarea in the document");
|
|
runCharAtPointAtOutsideTest();
|
|
runQueryTextContentEventTest();
|
|
runQuerySelectionEventTest();
|
|
runQueryIMESelectionTest();
|
|
runQueryContentEventRelativeToInsertionPoint();
|
|
runQueryPasswordTest();
|
|
runCSSTransformTest();
|
|
runPaddingLineBreakTest();
|
|
runBug722639Test();
|
|
runBug1375825Test();
|
|
runBug1530649Test();
|
|
runBug1571375Test();
|
|
runBug1675313Test();
|
|
runCommitCompositionWithSpaceKey();
|
|
runCompositionWithSelectionChange();
|
|
runCompositionWithClick();
|
|
runForceCommitTest();
|
|
runNestedSettingValue();
|
|
runBug811755Test();
|
|
runIsComposingTest();
|
|
runRedundantChangeTest();
|
|
runNotRedundantChangeTest();
|
|
runNativeLineBreakerTest();
|
|
runControlCharTest();
|
|
runFrameTest();
|
|
runMaxLengthTest();
|
|
|
|
window.close();
|
|
}
|
|
|
|
window.arguments[0].SimpleTest.waitForFocus(runTest, window);
|
|
|
|
]]>
|
|
</script>
|
|
|
|
</window>
|