mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 13:57:32 +00:00
Bug 1217700 part.3 Expose text change, selection change and position change notifications to nsITextInputProcessorCallback with nsITextInputProcessorNotification r=smaug
For testing IMEContentObserver, text change, selection change and position change notifications should be exposed to JS with nsITextInputProcessorNotification. MozReview-Commit-ID: 3PUhKXRwnAn --HG-- extra : rebase_source : fce7a73683a2d4811070453629ef48d3ad15c8c8
This commit is contained in:
parent
7b52d07cd2
commit
3d8bbc6723
@ -10,6 +10,7 @@
|
||||
#include "mozilla/TextEventDispatcher.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/TextInputProcessor.h"
|
||||
#include "mozilla/widget/IMEData.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIWidget.h"
|
||||
@ -27,12 +28,35 @@ namespace mozilla {
|
||||
class TextInputProcessorNotification final :
|
||||
public nsITextInputProcessorNotification
|
||||
{
|
||||
typedef IMENotification::SelectionChangeData SelectionChangeData;
|
||||
typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
|
||||
typedef IMENotification::TextChangeData TextChangeData;
|
||||
typedef IMENotification::TextChangeDataBase TextChangeDataBase;
|
||||
|
||||
public:
|
||||
explicit TextInputProcessorNotification(const char* aType)
|
||||
: mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TextInputProcessorNotification(
|
||||
const TextChangeDataBase& aTextChangeData)
|
||||
: mType("notify-text-change")
|
||||
, mTextChangeData(aTextChangeData)
|
||||
{
|
||||
}
|
||||
|
||||
explicit TextInputProcessorNotification(
|
||||
const SelectionChangeDataBase& aSelectionChangeData)
|
||||
: mType("notify-selection-change")
|
||||
, mSelectionChangeData(aSelectionChangeData)
|
||||
{
|
||||
// SelectionChangeDataBase::mString still refers nsString instance owned
|
||||
// by aSelectionChangeData. So, this needs to copy the instance.
|
||||
nsString* string = new nsString(aSelectionChangeData.String());
|
||||
mSelectionChangeData.mString = string;
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetType(nsACString& aType) override final
|
||||
@ -41,11 +65,216 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// "notify-text-change" and "notify-selection-change"
|
||||
NS_IMETHOD GetOffset(uint32_t* aOffset) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aOffset)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aOffset = mSelectionChangeData.mOffset;
|
||||
return NS_OK;
|
||||
}
|
||||
if (IsTextChange()) {
|
||||
*aOffset = mTextChangeData.mStartOffset;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// "notify-selection-change"
|
||||
NS_IMETHOD GetText(nsAString& aText) override final
|
||||
{
|
||||
if (IsSelectionChange()) {
|
||||
aText = mSelectionChangeData.String();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetCollapsed(bool* aCollapsed) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aCollapsed)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aCollapsed = mSelectionChangeData.IsCollapsed();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetLength(uint32_t* aLength) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aLength)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aLength = mSelectionChangeData.Length();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetReversed(bool* aReversed) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aReversed)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aReversed = mSelectionChangeData.mReversed;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetWritingMode(nsACString& aWritingMode) override final
|
||||
{
|
||||
if (IsSelectionChange()) {
|
||||
WritingMode writingMode = mSelectionChangeData.GetWritingMode();
|
||||
if (!writingMode.IsVertical()) {
|
||||
aWritingMode.AssignLiteral("horizontal-tb");
|
||||
} else if (writingMode.IsVerticalLR()) {
|
||||
aWritingMode.AssignLiteral("vertical-lr");
|
||||
} else {
|
||||
aWritingMode.AssignLiteral("vertical-rl");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetCausedByComposition(bool* aCausedByComposition) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aCausedByComposition)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aCausedByComposition = mSelectionChangeData.mCausedByComposition;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetCausedBySelectionEvent(
|
||||
bool* aCausedBySelectionEvent) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aCausedBySelectionEvent)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aCausedBySelectionEvent = mSelectionChangeData.mCausedBySelectionEvent;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetOccurredDuringComposition(
|
||||
bool* aOccurredDuringComposition) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aOccurredDuringComposition)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsSelectionChange()) {
|
||||
*aOccurredDuringComposition =
|
||||
mSelectionChangeData.mOccurredDuringComposition;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// "notify-text-change"
|
||||
NS_IMETHOD GetRemovedLength(uint32_t* aLength) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aLength)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsTextChange()) {
|
||||
*aLength = mTextChangeData.OldLength();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetAddedLength(uint32_t* aLength) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aLength)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsTextChange()) {
|
||||
*aLength = mTextChangeData.NewLength();
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetCausedOnlyByComposition(
|
||||
bool* aCausedOnlyByComposition) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aCausedOnlyByComposition)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsTextChange()) {
|
||||
*aCausedOnlyByComposition = mTextChangeData.mCausedOnlyByComposition;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetIncludingChangesDuringComposition(
|
||||
bool* aIncludingChangesDuringComposition) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aIncludingChangesDuringComposition)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsTextChange()) {
|
||||
*aIncludingChangesDuringComposition =
|
||||
mTextChangeData.mIncludingChangesDuringComposition;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetIncludingChangesWithoutComposition(
|
||||
bool* aIncludingChangesWithoutComposition) override final
|
||||
{
|
||||
if (NS_WARN_IF(!aIncludingChangesWithoutComposition)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (IsTextChange()) {
|
||||
*aIncludingChangesWithoutComposition =
|
||||
mTextChangeData.mIncludingChangesWithoutComposition;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
protected:
|
||||
~TextInputProcessorNotification() { }
|
||||
virtual ~TextInputProcessorNotification()
|
||||
{
|
||||
if (IsSelectionChange()) {
|
||||
delete mSelectionChangeData.mString;
|
||||
mSelectionChangeData.mString = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsTextChange() const
|
||||
{
|
||||
return mType.EqualsLiteral("notify-text-change");
|
||||
}
|
||||
|
||||
bool IsSelectionChange() const
|
||||
{
|
||||
return mType.EqualsLiteral("notify-selection-change");
|
||||
}
|
||||
|
||||
private:
|
||||
nsAutoCString mType;
|
||||
union
|
||||
{
|
||||
TextChangeDataBase mTextChangeData;
|
||||
SelectionChangeDataBase mSelectionChangeData;
|
||||
};
|
||||
|
||||
TextInputProcessorNotification() { }
|
||||
};
|
||||
@ -668,6 +897,18 @@ TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
||||
case NOTIFY_IME_OF_BLUR:
|
||||
notification = new TextInputProcessorNotification("notify-blur");
|
||||
break;
|
||||
case NOTIFY_IME_OF_TEXT_CHANGE:
|
||||
notification = new TextInputProcessorNotification(
|
||||
aNotification.mTextChangeData);
|
||||
break;
|
||||
case NOTIFY_IME_OF_SELECTION_CHANGE:
|
||||
notification = new TextInputProcessorNotification(
|
||||
aNotification.mSelectionChangeData);
|
||||
break;
|
||||
case NOTIFY_IME_OF_POSITION_CHANGE:
|
||||
notification = new TextInputProcessorNotification(
|
||||
"notify-position-change");
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@ -701,8 +942,10 @@ TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
|
||||
NS_IMETHODIMP_(IMENotificationRequests)
|
||||
TextInputProcessor::GetIMENotificationRequests()
|
||||
{
|
||||
// TextInputProcessor::NotifyIME does not require extra change notifications.
|
||||
return IMENotificationRequests();
|
||||
// TextInputProcessor should support all change notifications.
|
||||
return IMENotificationRequests(
|
||||
IMENotificationRequests::NOTIFY_TEXT_CHANGE |
|
||||
IMENotificationRequests::NOTIFY_POSITION_CHANGE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(void)
|
||||
|
@ -59,6 +59,8 @@ function onunload()
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
const kIsMac = (navigator.platform.indexOf("Mac") == 0);
|
||||
|
||||
var iframe = document.getElementById("iframe");
|
||||
var childWindow = iframe.contentWindow;
|
||||
var textareaInFrame;
|
||||
@ -3786,7 +3788,7 @@ function runCommitCompositionTests()
|
||||
description + "doCommitWithNullCheck(undefined) should commit the composition with empty string");
|
||||
}
|
||||
|
||||
function runUnloadTests1(aNextTest)
|
||||
function runUnloadTests1()
|
||||
{
|
||||
var description = "runUnloadTests1(): ";
|
||||
|
||||
@ -3808,7 +3810,7 @@ function runUnloadTests1(aNextTest)
|
||||
iframe.removeEventListener("load", arguments.callee, true);
|
||||
childWindow = iframe.contentWindow;
|
||||
textareaInFrame = iframe.contentDocument.getElementById("textarea");
|
||||
setTimeout(aNextTest, 0);
|
||||
SimpleTest.executeSoon(continueTest);
|
||||
}, true);
|
||||
|
||||
// The composition should be committed internally. So, another TIP should
|
||||
@ -3848,7 +3850,7 @@ function runUnloadTests1(aNextTest)
|
||||
iframe.src = "data:text/html,<body>dummy page</body>";
|
||||
}
|
||||
|
||||
function runUnloadTests2(aNextTest)
|
||||
function runUnloadTests2()
|
||||
{
|
||||
var description = "runUnloadTests2(): ";
|
||||
|
||||
@ -3870,7 +3872,7 @@ function runUnloadTests2(aNextTest)
|
||||
iframe.removeEventListener("load", arguments.callee, true);
|
||||
childWindow = iframe.contentWindow;
|
||||
textareaInFrame = iframe.contentDocument.getElementById("textarea");
|
||||
setTimeout(aNextTest, 0);
|
||||
SimpleTest.executeSoon(continueTest);
|
||||
}, true);
|
||||
|
||||
input.focus();
|
||||
@ -3911,7 +3913,7 @@ function runUnloadTests2(aNextTest)
|
||||
iframe.src = "data:text/html,<body>dummy page</body>";
|
||||
}
|
||||
|
||||
function runCallbackTests(aForTests)
|
||||
function* runCallbackTests(aForTests)
|
||||
{
|
||||
var description = "runCallbackTests(aForTests=" + aForTests + "): ";
|
||||
|
||||
@ -3921,8 +3923,12 @@ function runCallbackTests(aForTests)
|
||||
|
||||
var TIP = createTIP();
|
||||
var notifications = [];
|
||||
var callContinueTest = false;
|
||||
function callback(aTIP, aNotification)
|
||||
{
|
||||
if (aTIP == TIP) {
|
||||
notifications.push(aNotification);
|
||||
}
|
||||
switch (aNotification.type) {
|
||||
case "request-to-commit":
|
||||
aTIP.commitComposition();
|
||||
@ -3931,8 +3937,9 @@ function runCallbackTests(aForTests)
|
||||
aTIP.cancelComposition();
|
||||
break;
|
||||
}
|
||||
if (aTIP == TIP) {
|
||||
notifications.push(aNotification);
|
||||
if (callContinueTest) {
|
||||
callContinueTest = false;
|
||||
SimpleTest.executeSoon(continueTest);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3948,6 +3955,69 @@ function runCallbackTests(aForTests)
|
||||
}
|
||||
}
|
||||
|
||||
function waitUntilNotificationsReceived()
|
||||
{
|
||||
if (notifications.length > 0) {
|
||||
SimpleTest.executeSoon(continueTest);
|
||||
} else {
|
||||
callContinueTest = true;
|
||||
}
|
||||
}
|
||||
|
||||
function checkPositionChangeNotification(aNotification, aDescription)
|
||||
{
|
||||
is(!aNotification || aNotification.type, "notify-position-change",
|
||||
aDescription + " should cause position change notification");
|
||||
}
|
||||
|
||||
function checkSelectionChangeNotification(aNotification, aDescription, aExpected)
|
||||
{
|
||||
is(aNotification.type, "notify-selection-change",
|
||||
aDescription + " should cause selection change notification");
|
||||
if (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 == 0,
|
||||
aDescription + " should cause selection change notification whose collapsed is " + (aExpected.text.length == 0));
|
||||
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"));
|
||||
is(aNotification.causedByComposition, aExpected.causedByComposition || false,
|
||||
aDescription + " should cause selection change notification whose causedByComposition is " + (aExpected.causedByComposition || false));
|
||||
is(aNotification.causedBySelectionEvent, aExpected.causedBySelectionEvent || false,
|
||||
aDescription + " should cause selection change notification whose causedBySelectionEvent is " + (aExpected.causedBySelectionEvent || false));
|
||||
is(aNotification.occurredDuringComposition, aExpected.occurredDuringComposition || false,
|
||||
aDescription + " should cause cause selection change notification whose occurredDuringComposition is " + (aExpected.occurredDuringComposition || false));
|
||||
}
|
||||
|
||||
function checkTextChangeNotification(aNotification, aDescription, aExpected)
|
||||
{
|
||||
is(aNotification.type, "notify-text-change",
|
||||
aDescription + " should cause text change notification");
|
||||
if (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);
|
||||
is(aNotification.causedOnlyByComposition, aExpected.causedOnlyByComposition || false,
|
||||
aDescription + " should cause text change notification whose causedOnlyByComposition is " + (aExpected.causedOnlyByComposition || false));
|
||||
is(aNotification.includingChangesDuringComposition, aExpected.includingChangesDuringComposition || false,
|
||||
aDescription + " should cause text change notification whose includingChangesDuringComposition is " + (aExpected.includingChangesDuringComposition || false));
|
||||
is(aNotification.includingChangesWithoutComposition, typeof aExpected.includingChangesWithoutComposition === "boolean" ? aExpected.includingChangesWithoutComposition : true,
|
||||
aDescription + " should cause text change notification whose includingChangesWithoutComposition is " + (typeof aExpected.includingChangesWithoutComposition === "boolean" ? aExpected.includingChangesWithoutComposition : true));
|
||||
}
|
||||
|
||||
if (aForTests) {
|
||||
TIP.beginInputTransactionForTests(window, callback);
|
||||
} else {
|
||||
@ -3971,15 +4041,94 @@ function runCallbackTests(aForTests)
|
||||
dumpUnexpectedNotifications(1);
|
||||
|
||||
input.focus();
|
||||
notifications = [];
|
||||
TIP.setPendingCompositionString("foo");
|
||||
TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE);
|
||||
TIP.flushPendingComposition();
|
||||
is(notifications.length, 3,
|
||||
description + "creating composition string 'foo' should cause 3 notifications");
|
||||
checkTextChangeNotification(notifications[0], description + "creating composition string 'foo'",
|
||||
{ offset: 0, removedLength: 0, addedLength: 3,
|
||||
causedOnlyByComposition: true, includingChangesDuringComposition: false, includingChangesWithoutComposition: false});
|
||||
checkSelectionChangeNotification(notifications[1], description + "creating composition string 'foo'",
|
||||
{ offset: 3, text: "", causedByComposition: true, occurredDuringComposition: true });
|
||||
checkPositionChangeNotification(notifications[2], description + "creating composition string 'foo'");
|
||||
dumpUnexpectedNotifications(3);
|
||||
|
||||
notifications = [];
|
||||
synthesizeMouseAtCenter(input, {});
|
||||
is(notifications.length, 1,
|
||||
description + "synthesizeMouseAtCenter(input, {}) during composition should cause a notification");
|
||||
is(notifications.length, 3,
|
||||
description + "synthesizeMouseAtCenter(input, {}) during composition should cause 3 notifications");
|
||||
is(notifications[0].type, "request-to-commit",
|
||||
description + "synthesizeMouseAtCenter(input, {}) during composition should cause \"request-to-commit\"");
|
||||
checkTextChangeNotification(notifications[1], description + "synthesizeMouseAtCenter(input, {}) during composition",
|
||||
{ offset: 0, removedLength: 3, addedLength: 3,
|
||||
causedOnlyByComposition: true, includingChangesDuringComposition: false, includingChangesWithoutComposition: false});
|
||||
checkPositionChangeNotification(notifications[2], description + "synthesizeMouseAtCenter(input, {}) during composition");
|
||||
dumpUnexpectedNotifications(3);
|
||||
|
||||
// XXX On macOS, window.moveBy() doesn't cause notify-position-change.
|
||||
// Investigate this later (although, we cannot notify position change to
|
||||
// native IME on macOS).
|
||||
if (!kIsMac) {
|
||||
input.focus();
|
||||
notifications = [];
|
||||
window.moveBy(0, 10);
|
||||
yield waitUntilNotificationsReceived();
|
||||
is(notifications.length, 1,
|
||||
description + "window.moveBy(0, 10) should cause a notification");
|
||||
checkPositionChangeNotification(notifications[0], description + "window.moveBy(0, 10)");
|
||||
dumpUnexpectedNotifications(1);
|
||||
|
||||
input.focus();
|
||||
notifications = [];
|
||||
window.moveBy(10, 0);
|
||||
yield waitUntilNotificationsReceived();
|
||||
is(notifications.length, 1,
|
||||
description + "window.moveBy(10, 0) should cause a notification");
|
||||
checkPositionChangeNotification(notifications[0], description + "window.moveBy(10, 0)");
|
||||
dumpUnexpectedNotifications(1);
|
||||
}
|
||||
|
||||
input.focus();
|
||||
input.value = "abc"
|
||||
notifications = [];
|
||||
input.selectionStart = input.selectionEnd = 0;
|
||||
yield waitUntilNotificationsReceived();
|
||||
notifications = [];
|
||||
var rightArrowKeyEvent =
|
||||
new KeyboardEvent("", { key: "ArrowRight", code: "ArrowRight", keyCode: KeyboardEvent.DOM_VK_RIGHT });
|
||||
TIP.keydown(rightArrowKeyEvent);
|
||||
TIP.keyup(rightArrowKeyEvent);
|
||||
is(notifications.length, 1,
|
||||
description + "ArrowRight key press should cause a notification");
|
||||
checkSelectionChangeNotification(notifications[0], description + "ArrowRight key press", { offset: 1, text: "" });
|
||||
dumpUnexpectedNotifications(1);
|
||||
|
||||
notifications = [];
|
||||
var shiftKeyEvent =
|
||||
new KeyboardEvent("", { key: "Shift", code: "ShiftLeft", keyCode: KeyboardEvent.DOM_VK_SHIFT });
|
||||
var leftArrowKeyEvent =
|
||||
new KeyboardEvent("", { key: "ArrowLeft", code: "ArrowLeft", keyCode: KeyboardEvent.DOM_VK_LEFT });
|
||||
TIP.keydown(shiftKeyEvent);
|
||||
TIP.keydown(leftArrowKeyEvent);
|
||||
TIP.keyup(leftArrowKeyEvent);
|
||||
TIP.keyup(shiftKeyEvent);
|
||||
is(notifications.length, 1,
|
||||
description + "ArrowLeft key press with Shift should cause a notification");
|
||||
checkSelectionChangeNotification(notifications[0], description + "ArrowLeft key press with Shift", { offset: 0, text: "a", reversed: true });
|
||||
dumpUnexpectedNotifications(1);
|
||||
|
||||
TIP.keydown(rightArrowKeyEvent);
|
||||
TIP.keyup(rightArrowKeyEvent);
|
||||
notifications = [];
|
||||
TIP.keydown(shiftKeyEvent);
|
||||
TIP.keydown(rightArrowKeyEvent);
|
||||
TIP.keyup(rightArrowKeyEvent);
|
||||
TIP.keyup(shiftKeyEvent);
|
||||
is(notifications.length, 1,
|
||||
description + "ArrowRight key press with Shift should cause a notification");
|
||||
checkSelectionChangeNotification(notifications[0], description + "ArrowRight key press with Shift", { offset: 1, text: "b" });
|
||||
dumpUnexpectedNotifications(1);
|
||||
|
||||
notifications = [];
|
||||
@ -3996,10 +4145,21 @@ function runCallbackTests(aForTests)
|
||||
dumpUnexpectedNotifications(1);
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
textareaInFrame = iframe.contentDocument.getElementById("textarea");
|
||||
var gTestContinuation = null;
|
||||
|
||||
function continueTest()
|
||||
{
|
||||
if (!gTestContinuation) {
|
||||
gTestContinuation = testBody();
|
||||
}
|
||||
var ret = gTestContinuation.next();
|
||||
if (ret.done) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
function* testBody()
|
||||
{
|
||||
runBeginInputTransactionMethodTests();
|
||||
runReleaseTests();
|
||||
runCompositionTests();
|
||||
@ -4008,13 +4168,16 @@ function runTests()
|
||||
runKeyTests();
|
||||
runErrorTests();
|
||||
runCommitCompositionTests();
|
||||
runCallbackTests(false);
|
||||
runCallbackTests(true);
|
||||
runUnloadTests1(function () {
|
||||
runUnloadTests2(function () {
|
||||
finish();
|
||||
});
|
||||
});
|
||||
yield* runCallbackTests(false);
|
||||
yield* runCallbackTests(true);
|
||||
yield runUnloadTests1();
|
||||
yield runUnloadTests2();
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
textareaInFrame = iframe.contentDocument.getElementById("textarea");
|
||||
continueTest();
|
||||
}
|
||||
|
||||
]]>
|
||||
|
@ -44,8 +44,143 @@ interface nsITextInputProcessorNotification : nsISupports
|
||||
* "notify-blur" (optional)
|
||||
* This is notified when an editable editor loses focus and Gecko stops
|
||||
* observing the changes in the content.
|
||||
*
|
||||
* "notify-text-change" (optional)
|
||||
* This is notified when text in the focused editor is modified.
|
||||
* Some attributes below are available to retrieve the detail.
|
||||
* IME shouldn't change DOM tree, focus nor something when this is notified.
|
||||
* Note that when there is no chance to notify you of some text changes
|
||||
* safely, this represents all changes as a change.
|
||||
*
|
||||
* "notify-selection-change" (optional)
|
||||
* This is notified when selection in the focused editor is changed.
|
||||
* Some attributes below are available to retrieve the detail.
|
||||
* IME shouldn't change DOM tree, focus nor something when this is notified.
|
||||
* Note that when there was no chance to notify you of this safely, this
|
||||
* represents the latest selection change.
|
||||
*
|
||||
* "notify-position-change" (optional)
|
||||
* This is notified when layout is changed in the editor or the window
|
||||
* is moved.
|
||||
* IME shouldn't change DOM tree, focus nor something when this is notified.
|
||||
* Note that when there was no chance to notify you of this safely, this
|
||||
* represents the latest layout change.
|
||||
*/
|
||||
readonly attribute ACString type;
|
||||
|
||||
/**
|
||||
* Be careful, line breakers in the editor are treated as native line
|
||||
* breakers. I.e., on Windows, a line breaker is "\r\n" (CRLF). On the
|
||||
* others, it is "\n" (LF). If you want TextInputProcessor to treat line
|
||||
* breakers on Windows as XP line breakers (LF), please file a bug with
|
||||
* the reason why you need the behavior.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-text-change" or
|
||||
* "notify-selection-change".
|
||||
* This is offset of the start of modified text range if type is
|
||||
* "notify-text-change". Or offset of start of selection if type is
|
||||
* "notify-selection-change".
|
||||
*/
|
||||
readonly attribute unsigned long offset;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* This is selected text. I.e., the length is selected length and
|
||||
* it's empty if the selection is collapsed.
|
||||
*/
|
||||
readonly attribute AString text;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* This is set to true when the selection is collapsed. Otherwise, false.
|
||||
*/
|
||||
readonly attribute boolean collapsed;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* This is selected length. I.e., if this is 0, collapsed is set to true.
|
||||
*/
|
||||
readonly attribute uint32_t length;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* When selection is created from latter point to former point, this is
|
||||
* set to true. Otherwise, false.
|
||||
* I.e., if this is true, offset + length is the anchor of selection.
|
||||
*/
|
||||
readonly attribute boolean reversed;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* This indicates the start of the selection's writing mode.
|
||||
* The value can be "horizontal-tb", "vertical-rl" or "vertical-lr".
|
||||
*/
|
||||
readonly attribute ACString writingMode;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* If the selection change was caused by composition, this is set to true.
|
||||
* Otherwise, false.
|
||||
*/
|
||||
readonly attribute boolean causedByComposition;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* If the selection change was caused by selection event, this is set to true.
|
||||
* Otherwise, false.
|
||||
*/
|
||||
readonly attribute boolean causedBySelectionEvent;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-selection-change".
|
||||
* If the selection change occurred during composition, this is set to true.
|
||||
* Otherwise, false.
|
||||
*/
|
||||
readonly attribute boolean occurredDuringComposition;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-text-change".
|
||||
* This is removed text length by the change(s). If this is empty, new text
|
||||
* was just inserted. Otherwise, the text is replaced with new text.
|
||||
*/
|
||||
readonly attribute unsigned long removedLength;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-text-change".
|
||||
* This is added text length by the change(s). If this is empty, old text
|
||||
* was just deleted. Otherwise, the text replaces the old text.
|
||||
*/
|
||||
readonly attribute unsigned long addedLength;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-text-change".
|
||||
* If the text change(s) was caused only by composition, this is set to true.
|
||||
* Otherwise, false. I.e., if one of text changes are caused by JS or
|
||||
* modifying without composition, this is set to false.
|
||||
*/
|
||||
readonly attribute boolean causedOnlyByComposition;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-text-change".
|
||||
* If at least one text change not caused by composition occurred during
|
||||
* composition, this is set to true. Otherwise, false.
|
||||
* Note that this is set to false when new change is caused by neither
|
||||
* composition nor occurred during composition because it's outdated for
|
||||
* new composition.
|
||||
* In other words, when text changes not caused by composition occurred
|
||||
* during composition and it may cause committing composition, this is
|
||||
* set to true.
|
||||
*/
|
||||
readonly attribute boolean includingChangesDuringComposition;
|
||||
|
||||
/**
|
||||
* This attribute has a valid value when type is "notify-text-change".
|
||||
* If at least one text change occurred when there was no composition, this
|
||||
* is set to true. Otherwise, false.
|
||||
*/
|
||||
readonly attribute boolean includingChangesWithoutComposition;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user