forked from mirrors/gecko-dev
This is another regression by bug 1658948 and Windows only. When user script calls element.focus() during keyboard event, our TSF client implementation commits composition string. It is unnecessary to call SetInputContext when real keybaord event is fired. Because, - If keybaord event is fired, virtual keybaord is already shown - We don't open virtual keyboard when physical keyboard is available on Android So I shouldn't call SetInputContext on user interaction by keyboard. Differential Revision: https://phabricator.services.mozilla.com/D98882
9632 lines
442 KiB
HTML
9632 lines
442 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">
|
|
<p 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/>
|
|
<iframe id="iframe5" width="200" height="50" src="data:text/html,<input id='input'>"></iframe>
|
|
<iframe id="iframe6" width="200" 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/>
|
|
</p>
|
|
<div id="content" style="display: none">
|
|
|
|
</div>
|
|
<pre id="test">
|
|
</pre>
|
|
</body>
|
|
|
|
<script class="testbody" type="application/javascript">
|
|
<![CDATA[
|
|
|
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
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 ? 2 : 1;
|
|
const kLF = kIsWin ? "\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;
|
|
}
|
|
is(selectedText.offset, aExpectedOffset,
|
|
aMessage + ": selection offset is wrong " + aID);
|
|
is(selectedText.text, aExpectedText,
|
|
aMessage + ": selected text is wrong " + aID);
|
|
return selectedText.offset == aExpectedOffset &&
|
|
selectedText.text == aExpectedText;
|
|
}
|
|
|
|
function checkIMESelection(aSelectionType, aExpectedFound, aExpectedOffset, aExpectedText, aMessage, aID)
|
|
{
|
|
if (!aID) {
|
|
aID = "";
|
|
}
|
|
aMessage += " (" + aSelectionType + ")";
|
|
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;
|
|
}
|
|
is(selectedText.notFound, !aExpectedFound,
|
|
aMessage + ": selection should " + (aExpectedFound ? "" : "not") + " be found " + aID);
|
|
if (selectedText.notFound) {
|
|
return selectedText.notFound == !aExpectedFound;
|
|
}
|
|
|
|
is(selectedText.offset, aExpectedOffset,
|
|
aMessage + ": selection offset is wrong " + aID);
|
|
is(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 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(aEvent instanceof InputEvent, `"${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 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("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");
|
|
|
|
clearResult();
|
|
synthesizeComposition({ type: "compositioncommitasis", key: { key: "Enter" } });
|
|
|
|
is(result.length, 4,
|
|
"runCompositionCommitAsIsTest: 4 events should be fired after dispatching compositioncommitasis #1");
|
|
is(result[0].type, "text",
|
|
"runCompositionCommitAsIsTest: text should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1");
|
|
is(result[1].type, "beforeinput",
|
|
"runCompositionCommitAsIsTest: beforeinput should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "\u3042", [],
|
|
"runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1");
|
|
is(result[2].type, "compositionend",
|
|
"runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #1");
|
|
is(result[3].type, "input",
|
|
"runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #1");
|
|
checkInputEvent(result[3], 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");
|
|
|
|
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");
|
|
|
|
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("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("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");
|
|
|
|
clearResult();
|
|
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "a", type: "keyup" } });
|
|
|
|
is(result.length, 5,
|
|
"runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #1");
|
|
is(result[0].type, "compositionupdate",
|
|
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit because it's dispatched when there is composing string #1");
|
|
is(result[1].type, "text",
|
|
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #1");
|
|
is(result[2].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit because it's dispatched when there is composing string #1");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "\u3043", [],
|
|
"runCompositionCommitTest: after dispatching compositioncommit #1");
|
|
is(result[3].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #1");
|
|
is(result[4].type, "input",
|
|
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #1");
|
|
checkInputEvent(result[4], 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 = "";
|
|
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");
|
|
|
|
clearResult();
|
|
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } });
|
|
|
|
is(result.length, 5,
|
|
"runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #2");
|
|
is(result[0].type, "compositionupdate",
|
|
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #2");
|
|
is(result[1].type, "text",
|
|
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #2");
|
|
is(result[2].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #2");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "\u3043", [],
|
|
"runCompositionCommitTest: after dispatching compositioncommit #2");
|
|
is(result[3].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #2");
|
|
is(result[4].type, "input",
|
|
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #2");
|
|
checkInputEvent(result[4], 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 = "";
|
|
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");
|
|
|
|
clearResult();
|
|
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } });
|
|
|
|
is(result.length, 5,
|
|
"runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #3");
|
|
is(result[0].type, "compositionupdate",
|
|
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #3");
|
|
is(result[1].type, "text",
|
|
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #3");
|
|
is(result[2].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #3");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "\u3043", [],
|
|
"runCompositionCommitTest: after dispatching compositioncommit #3");
|
|
is(result[3].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #3");
|
|
is(result[4].type, "input",
|
|
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #3");
|
|
checkInputEvent(result[4], 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, 4,
|
|
"runCompositionCommitTest: 4 events should be fired when inserting empty string with composition");
|
|
is(result[0].type, "text",
|
|
"runCompositionCommitTest: text should be fired when inserting empty string with composition");
|
|
is(result[1].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired when inserting empty string with composition");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "", [],
|
|
"runCompositionCommitTest: when inserting empty string with composition");
|
|
is(result[2].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired when inserting empty string with composition");
|
|
is(result[3].type, "input",
|
|
"runCompositionCommitTest: input should be fired when inserting empty string with composition");
|
|
checkInputEvent(result[3], 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, 4,
|
|
"runCompositionCommitTest: 4 events should be fired when replacing with empty string with composition");
|
|
is(result[0].type, "text",
|
|
"runCompositionCommitTest: text should be fired when replacing with empty string with composition");
|
|
is(result[1].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired when replacing with empty string with composition");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "", [],
|
|
"runCompositionCommitTest: when replacing with empty string with composition");
|
|
is(result[2].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired when replacing with empty string with composition");
|
|
is(result[3].type, "input",
|
|
"runCompositionCommitTest: input should be fired when replacing with empty string with composition");
|
|
checkInputEvent(result[3], 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, 5,
|
|
"runCompositionCommitTest: 5 events should be fired when replacing selection with same string with composition");
|
|
is(result[0].type, "compositionupdate",
|
|
"runCompositionCommitTest: compositionupdate should be fired when replacing selection with same string with composition");
|
|
is(result[1].type, "text",
|
|
"runCompositionCommitTest: text should be fired when replacing selection with same string with composition");
|
|
is(result[2].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired when replacing selection with same string with composition");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "abc", [],
|
|
"runCompositionCommitTest: when replacing selection with same string with composition");
|
|
is(result[3].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired when replacing selection with same string with composition");
|
|
is(result[4].type, "input",
|
|
"runCompositionCommitTest: input should be fired when replacing selection with same string with composition");
|
|
checkInputEvent(result[4], 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, 5,
|
|
"runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #4");
|
|
is(result[0].type, "compositionupdate",
|
|
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #4");
|
|
is(result[1].type, "text",
|
|
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #4");
|
|
is(result[2].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #4");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "", [],
|
|
"runCompositionCommitTest: after dispatching compositioncommit #4");
|
|
is(result[3].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #4");
|
|
is(result[4].type, "input",
|
|
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #4");
|
|
checkInputEvent(result[4], 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, 5,
|
|
"runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #5");
|
|
is(result[0].type, "compositionupdate",
|
|
"runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #5");
|
|
is(result[1].type, "text",
|
|
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #5");
|
|
is(result[2].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #5");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "\u3042", [],
|
|
"runCompositionCommitTest: after dispatching compositioncommit #5");
|
|
is(result[3].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
|
|
is(result[4].type, "input",
|
|
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #5");
|
|
checkInputEvent(result[4], 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, 4,
|
|
"runCompositionCommitTest: 4 events should be fired after dispatching compositioncommit #6");
|
|
is(result[0].type, "text",
|
|
"runCompositionCommitTest: text should be fired after dispatching compositioncommit #6");
|
|
is(result[1].type, "beforeinput",
|
|
"runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #6");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "\u3042", [],
|
|
"runCompositionCommitTest: after dispatching compositioncommit #6");
|
|
is(result[2].type, "compositionend",
|
|
"runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #6");
|
|
is(result[3].type, "input",
|
|
"runCompositionCommitTest: input should be fired after dispatching compositioncommit #6");
|
|
checkInputEvent(result[3], 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 = "";
|
|
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");
|
|
|
|
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("input", handler, true);
|
|
textarea.removeEventListener("text", handler, true);
|
|
}
|
|
|
|
// eslint-disable-next-line complexity
|
|
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
|
|
let selectionSetTest = 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, "\u6700", "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, 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, 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, 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, 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, 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, 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, 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, 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);
|
|
}
|
|
|
|
// 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");
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
function runSetSelectionEventTest()
|
|
{
|
|
contenteditable.focus();
|
|
|
|
let selection = windowOfContenteditable.getSelection();
|
|
|
|
// #1
|
|
contenteditable.innerHTML = "abc<br>def";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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 = "";
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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>";
|
|
|
|
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>";
|
|
|
|
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>";
|
|
|
|
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";
|
|
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>";
|
|
|
|
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 + "\"");
|
|
|
|
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>";
|
|
|
|
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 + "\"");
|
|
}
|
|
|
|
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(aEvent)
|
|
{
|
|
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<br>",
|
|
"runBug1530649Test: The new composition string shouldn't remove the last space");
|
|
|
|
synthesizeComposition({type: "compositioncommitasis", key: "KEY_Enter"});
|
|
|
|
is(contenteditable.innerHTML, "abc d<br>",
|
|
"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);
|
|
if (!checkQueryContentResult(lastCharRectTranslatedAsArray, "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform) ||
|
|
!checkRectArray(lastCharRectTranslatedAsArray, [lastCharRectTranslated], "runCSSTransformTest: lastCharRectTranslatedAsArray, " + textarea.style.transform)) {
|
|
return;
|
|
}
|
|
} 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 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, 4,
|
|
"runForceCommitTest: wrong event count #2");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #2");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #2`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #2");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #2");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #2");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #2`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #2");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #2");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[3], 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, 4,
|
|
"runForceCommitTest: wrong event count #3");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #3");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #3`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #3");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #3`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #3");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #3");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #3`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #3");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #3");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #3`);
|
|
checkInputEvent(events[3], 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, 4,
|
|
"runForceCommitTest: wrong event count #4");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #4");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #4`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #4");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #4");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #4");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #4`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #4");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #4");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[3], 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, 4,
|
|
"runForceCommitTest: wrong event count #5");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #5");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #5`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #5");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #5`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #5");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #5");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #5`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #5");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #5");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #5`);
|
|
checkInputEvent(events[3], 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, 4,
|
|
"runForceCommitTest: wrong event count #6");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #6");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #6`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #6");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #6`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #6");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #6");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #6`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #6");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #6");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #6`);
|
|
checkInputEvent(events[3], 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, 4,
|
|
"runForceCommitTest: wrong event count #7");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #7");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #7`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #7");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #7`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #7");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #7");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #7`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #7");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #7");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #7`);
|
|
checkInputEvent(events[3], 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";
|
|
let 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, 4,
|
|
"runForceCommitTest: wrong event count #8");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #8");
|
|
is(events[0].target, textarea,
|
|
`runForceCommitTest: The ${events[0].type} event was fired on wrong event target #8`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #8");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The ${events[1].type} event was fired on wrong event target #8`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #8");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #8");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The ${events[2].type} event was fired on wrong event target #8`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #8");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #8");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The ${events[3].type} event was fired on wrong event target #8`);
|
|
checkInputEvent(events[3], 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";
|
|
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, 4,
|
|
"runForceCommitTest: wrong event count #9");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #9");
|
|
is(events[0].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #9`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #9");
|
|
is(events[1].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #9`);
|
|
checkInputEvent(events[1], 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");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #9");
|
|
is(events[2].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #9`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #9");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #9");
|
|
is(events[3].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #9`);
|
|
checkInputEvent(events[3], 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";
|
|
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, 4,
|
|
"runForceCommitTest: wrong event count #10");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #10");
|
|
is(events[0].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[0].type} event was fired on wrong event target #10`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #10");
|
|
is(events[1].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[1].type} event was fired on wrong event target #10`);
|
|
checkInputEvent(events[1], 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");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #10");
|
|
is(events[2].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[2].type} event was fired on wrong event target #10`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #10");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #10");
|
|
is(events[3].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The ${events[3].type} event was fired on wrong event target #10`);
|
|
checkInputEvent(events[3], 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";
|
|
iframe2BodyInnerHTML = iframe2.contentDocument.body.innerHTML;
|
|
iframe3.contentDocument.body.innerHTML = "Text in the Body";
|
|
let 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, 4,
|
|
"runForceCommitTest: wrong event count #11");
|
|
is(events[0].type, "text",
|
|
"runForceCommitTest: the 1st event must be text #11");
|
|
is(events[0].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[0].type}" event was fired on wrong event target #11`);
|
|
is(events[1].type, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #11");
|
|
is(events[1].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #11`);
|
|
checkInputEvent(events[1], 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");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #11");
|
|
is(events[2].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #11`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #11");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #11");
|
|
is(events[3].target, iframe2.contentDocument.body,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #11`);
|
|
checkInputEvent(events[3], 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";
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #12");
|
|
is(events[1].target, input,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #12`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #12");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #12");
|
|
is(events[2].target, input,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #12`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #12");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #12");
|
|
is(events[3].target, input,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #12`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #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";
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #13");
|
|
is(events[1].target, textarea,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #13`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #13");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #13");
|
|
is(events[2].target, textarea,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #13`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #13");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #13");
|
|
is(events[3].target, textarea,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #13`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #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";
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runForceCommitTest: the 2nd event must be beforeinput #14");
|
|
is(events[1].target, input,
|
|
`runForceCommitTest: The "${events[1].type}" event was fired on wrong event target #14`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #14");
|
|
is(events[2].type, "compositionend",
|
|
"runForceCommitTest: the 3rd event must be compositionend #14");
|
|
is(events[2].target, input,
|
|
`runForceCommitTest: The "${events[2].type}" event was fired on wrong event target #14`);
|
|
is(events[2].data, "\u306E",
|
|
"runForceCommitTest: compositionend has wrong data #14");
|
|
is(events[3].type, "input",
|
|
"runForceCommitTest: the 4th event must be input #14");
|
|
is(events[3].target, input,
|
|
`runForceCommitTest: The "${events[3].type}" event was fired on wrong event target #14`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runForceCommitTest #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;
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runNestedSettingValue: the 2nd event must be beforeinput #1");
|
|
is(events[1].target, textarea,
|
|
`runNestedSettingValue: The "${events[1].type}" event was fired on wrong event target #1`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #1");
|
|
is(events[2].type, "compositionend",
|
|
"runNestedSettingValue: the 3rd event must be compositionend #1");
|
|
is(events[2].target, textarea,
|
|
`runNestedSettingValue: The "${events[2].type}" event was fired on wrong event target #1`);
|
|
is(events[2].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #1");
|
|
is(events[3].type, "input",
|
|
"runNestedSettingValue: the 4th event must be input #1");
|
|
is(events[3].target, textarea,
|
|
`runNestedSettingValue: The "${events[3].type}" event was fired on wrong event target #1`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #1");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runNestedSettingValue: the textarea still has composition #1");
|
|
is(textarea.value, "first setting value, text, beforeinput, compositionend, input, ",
|
|
"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;
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runNestedSettingValue: the 2nd event must be beforeinput #2");
|
|
is(events[1].target, input,
|
|
`runNestedSettingValue: The "${events[1].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #2");
|
|
is(events[2].type, "compositionend",
|
|
"runNestedSettingValue: the 3rd event must be compositionend #2");
|
|
is(events[2].target, input,
|
|
`runNestedSettingValue: The "${events[2].type}" event was fired on wrong event target #2`);
|
|
is(events[2].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #2");
|
|
is(events[3].type, "input",
|
|
"runNestedSettingValue: the 4th event must be input #2");
|
|
is(events[3].target, input,
|
|
`runNestedSettingValue: The "${events[3].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #2");
|
|
ok(!getEditor(input).isComposing,
|
|
"runNestedSettingValue: the input still has composition #2");
|
|
is(textarea.value, "first setting value, text, beforeinput, compositionend, input, ",
|
|
"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;
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runNestedSettingValue: the 2nd event must be beforeinput #3");
|
|
is(events[1].target, textarea,
|
|
`runNestedSettingValue: The ${events[1].type} event was fired on wrong event target #3`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #3");
|
|
is(events[2].type, "compositionend",
|
|
"runNestedSettingValue: the 3rd event must be compositionend #3");
|
|
is(events[2].target, textarea,
|
|
`runNestedSettingValue: The ${events[2].type} event was fired on wrong event target #3`);
|
|
is(events[2].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #3");
|
|
is(events[3].type, "input",
|
|
"runNestedSettingValue: the 4th event must be input #3");
|
|
is(events[3].target, textarea,
|
|
`runNestedSettingValue: The ${events[3].type} event was fired on wrong event target #3`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #3");
|
|
ok(!getEditor(textarea).isComposing,
|
|
"runNestedSettingValue: the textarea still has composition #3");
|
|
is(textarea.value, "\u306Efirst setting value, text, beforeinput, compositionend, input, ",
|
|
"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;
|
|
|
|
is(events.length, 4,
|
|
"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, "beforeinput",
|
|
"runNestedSettingValue: the 2nd event must be beforeinput #4");
|
|
is(events[1].target, input,
|
|
`runNestedSettingValue: The "${events[1].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #4");
|
|
is(events[2].type, "compositionend",
|
|
"runNestedSettingValue: the 3rd event must be compositionend #4");
|
|
is(events[2].target, input,
|
|
`runNestedSettingValue: The "${events[2].type}" event was fired on wrong event target #4`);
|
|
is(events[2].data, "\u306E",
|
|
"runNestedSettingValue: compositionend has wrong data #4");
|
|
is(events[3].type, "input",
|
|
"runNestedSettingValue: the 4th event must be input #4");
|
|
is(events[3].target, input,
|
|
`runNestedSettingValue: The "${events[3].type}" event was fired on wrong event target #4`);
|
|
checkInputEvent(events[3], false, "insertCompositionText", "\u306E", [],
|
|
"runNestedSettingValue #4");
|
|
ok(!getEditor(input).isComposing,
|
|
"runNestedSettingValue: the input still has composition #4");
|
|
is(textarea.value, "\u306Efirst setting value, text, beforeinput, compositionend, input, ",
|
|
"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, 4,
|
|
"runAsyncForceCommitTest: wrong event count #2");
|
|
is(events[0].type, "text",
|
|
"runAsyncForceCommitTest: the 1st event must be text #2");
|
|
is(events[0].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[0].type}" event was fired on wrong event target #2`);
|
|
is(events[1].type, "beforeinput",
|
|
"runAsyncForceCommitTest: the 2nd event must be beforeinput #2");
|
|
is(events[1].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[1].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[1], true, "insertCompositionText", "\u306E", [],
|
|
"runAsyncForceCommitTest #2");
|
|
is(events[2].type, "compositionend",
|
|
"runAsyncForceCommitTest: the 3rd event must be compositionend #2");
|
|
is(events[2].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[2].type}" event was fired on wrong event target #2`);
|
|
is(events[2].data, "\u306E",
|
|
"runAsyncForceCommitTest: compositionend has wrong data #2");
|
|
is(events[3].type, "input",
|
|
"runAsyncForceCommitTest: the 4th event must be input #2");
|
|
is(events[3].target, textarea,
|
|
`runAsyncForceCommitTest: The "${events[3].type}" event was fired on wrong event target #2`);
|
|
checkInputEvent(events[3], 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).
|
|
Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
|
|
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"});
|
|
|
|
Services.prefs.clearUserPref("dom.keyboardevent.dispatch_during_composition");
|
|
|
|
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, 4,
|
|
"runRedundantChangeTest: 4 events should be fired after synthesizing composition commit-as-is");
|
|
is(result[0].type, "text",
|
|
"runRedundantChangeTest: text should be fired after synthesizing composition commit-as-is for removing the ranges");
|
|
is(result[1].type, "beforeinput",
|
|
"runRedundantChangeTest: beforeinput should be fired after synthesizing composition commit-as-is for removing the ranges");
|
|
checkInputEvent(result[1], true, "insertCompositionText", "\u3042\u3044", [],
|
|
"runRedundantChangeTest: at synthesizing commit-as-is");
|
|
is(result[2].type, "compositionend",
|
|
"runRedundantChangeTest: compositionend should be fired after synthesizing composition commit-as-is");
|
|
is(result[3].type, "input",
|
|
"runRedundantChangeTest: input should be fired before compositionend at synthesizing commit-as-is");
|
|
checkInputEvent(result[3], 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, 5,
|
|
"runNotRedundantChangeTest: 5 events should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
is(result[0].type, "compositionupdate",
|
|
"runNotRedundantChangeTest: compositionupdate should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
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 after non-empty data");
|
|
is(result[2].type, "beforeinput",
|
|
"runNotRedundantChangeTest: beforeinput should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
checkInputEvent(result[2], true, "insertCompositionText", "", [],
|
|
"runNotRedundantChangeTest: after synthesizing composition change with empty data after non-empty data");
|
|
is(result[3].type, "compositionend",
|
|
"runNotRedundantChangeTest: compositionend should be fired after synthesizing composition commit with empty data after non-empty data");
|
|
is(result[4].type, "input",
|
|
"runNotRedundantChangeTest: input should be fired after compositionend after synthesizing composition change with empty data after non-empty data");
|
|
checkInputEvent(result[4], 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":
|
|
// eslint-disable-next-line no-unsanitized/property
|
|
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: kLFLen });
|
|
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();
|
|
|
|
// "[]"
|
|
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) {
|
|
// Splitting current block causes removing "<block>aB" and inserting "<block>aB</block><block>".
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer - kLFLen, removedLength: getNativeText("\naB").length, addedLength: getNativeText("\naB\n").length });
|
|
} else {
|
|
// Oddly, inserting <br> causes removing "aB" and inserting "ab<br>".
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: 2, addedLength: getNativeText("ab\n").length });
|
|
}
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: getNativeText("aB\n").length + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
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();
|
|
if (isDefaultParagraphSeparatorBlock) {
|
|
// Joining two blocks causes removing both block elements and inserting new block element.
|
|
checkTextChangeNotification(notifications[0], description, { offset: offsetAtContainer - kLFLen, removedLength: getNativeText("\naB\nd").length, addedLength: getNativeText("\naBd").length });
|
|
checkSelectionChangeNotification(notifications[1], description, { offset: 2 + offsetAtContainer, text: "" });
|
|
checkPositionChangeNotification(notifications[2], description);
|
|
dumpUnexpectedNotifications(description, 3);
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, { offset: 2 + offsetAtContainer, 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) {
|
|
// Splitting current block causes removing "<block>aB" and inserting "<block>aB</block><block>".
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer - kLFLen, removedLength: getNativeText("\naB").length, addedLength: getNativeText("\na\n").length });
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, removedLength: 1, 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"
|
|
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>a</block><block>d</block>" and inserting "<block>ad</block>".
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer - kLFLen, removedLength: getNativeText("\na\nd").length, addedLength: getNativeText("\nad").length });
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, { offset: 1 + offsetAtContainer, 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\n").length, addedLength: getNativeText("\n1\n2\n345").length });
|
|
} else {
|
|
checkTextChangeNotification(notifications[0], description, { offset: 0 + offsetAtContainer, removedLength: 2 + kLFLen, 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>'");
|
|
// It causes removing '1<div>2<div>3</div></div>' and inserting '13<div>'.
|
|
checkTextChangeNotification(notifications[0], description, { offset: kLFLen + offsetAtStart, removedLength: getNativeText("1\n2\n3").length, addedLength: getNativeText("13\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 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 runTest()
|
|
{
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["dom.input_events.beforeinput.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();
|
|
|
|
runUndoRedoTest();
|
|
runCompositionTest();
|
|
runCompositionCommitAsIsTest();
|
|
runCompositionCommitTest();
|
|
runCompositionEventTest();
|
|
runQueryTextRectInContentEditableTest();
|
|
runCharAtPointTest(textarea, "textarea in the document");
|
|
runCharAtPointAtOutsideTest();
|
|
runSetSelectionEventTest();
|
|
runQueryTextContentEventTest();
|
|
runQuerySelectionEventTest();
|
|
runQueryIMESelectionTest();
|
|
runQueryContentEventRelativeToInsertionPoint();
|
|
runQueryPasswordTest();
|
|
runCSSTransformTest();
|
|
runBug722639Test();
|
|
runBug1375825Test();
|
|
runBug1530649Test();
|
|
runBug1571375Test();
|
|
runBug1675313Test();
|
|
runCommitCompositionWithSpaceKey();
|
|
runForceCommitTest();
|
|
runNestedSettingValue();
|
|
runBug811755Test();
|
|
runIsComposingTest();
|
|
runRedundantChangeTest();
|
|
runNotRedundantChangeTest();
|
|
runNativeLineBreakerTest();
|
|
runControlCharTest();
|
|
runFrameTest();
|
|
runMaxLengthTest();
|
|
|
|
window.close();
|
|
}
|
|
|
|
window.arguments[0].SimpleTest.waitForFocus(runTest, window);
|
|
|
|
]]>
|
|
</script>
|
|
|
|
</window>
|