nsISupports
Last changed in Gecko 38.0 (Firefox 38.0 / Thunderbird 38.0 / SeaMonkey 2.35)The motivation of this interface is to provide better API than nsIDOMWindowUtils to dispatch key events and create, modify, and commit composition in higher level. nsIDOMWindowUtils has provided the methods which dispatched keyboard events and composition events almost directly. Therefore they sometimes caused impossible scenarios in automated tests (what's tested with such events?) and JS-IME and/or JS-Keyboard on Firefox OS or add-ons may dispatch events with wrong rules. For solving that issue, methods of this interface have been designed for performing a key operation or representing a change of composition state.
For example, the implementation of this interface manages modifier state and composition state, initializes DOM events from minimum information, and doesn't dispatch some events if they are not necessary. This means that even when Gecko changes the DOM event behavior, it may not be necessary that the users of this interface need to be updated, i.e., the implementation of this class can be a cushion from the impact of Gecko's change.
You can create an instance of this interface with the following code:
var TIP = Components.classes["@mozilla.org/text-input-processor;1"]. createInstance(Components.interfaces.nsITextInputProcessor);
Next, you need to get the rights to create composition or dispatch keyboard events with beginInputTransaction() or beginInputTransactionForTests():
if (!TIP.beginInputTransaction(window, callback)) { return; }
If beginInputTransaction() or beginInputTransactionForTests() returns false, it means that another instance of nsITextInputProcessor has composition on the window or is dispatching an event. In this case, other instances cannot create composition nor dispatch keyboard events on the window.
The second argument, callback, should be an object which implements nsITextInputProcessorCallback
or just a function which is the same as nsITextInputProcessorCallback.onNotify(). See the document of nsITextInputProcessorCallback
for the detail.
Next, you can set the composition string or dispatch keyboard events.
The following example sets "foo-bar-buzz", "bar" is selected clause to convert, and caret position is the end of the selected clause:
// First, sets composition string. TIP.setPendingCompositionString("foo-bar-buzz"); // Next, append clauses. TIP.appendClauseToPendingComposition("foo-".length, TIP.ATTR_CONVERTED_CLAUSE); TIP.appendClauseToPendingComposition("bar".length, TIP.ATTR_SELECTED_CLAUSE); TIP.appendClauseToPendingComposition("-buzz".length, TIP.ATTR_CONVERTED_CLAUSE); // Then, sets the caret if you need TIP.setCaretInPendingComposition("foo-bar".length); // Finally, flush the pending composition on the focused editor if (!TIP.flushPendingComposition()) { return; // Composition is not started }
If there is no composition, flushPendingComposition() starts composition with dispatching compositionstart
event automatically. However, if the event is consumed by web content, it returns false. In this case, composition isn't created. So, JS-IME shouldn't continue to compose the string.
Finally, when you commit the composition with the last composition string, just do this:
TIP.commitComposition();
When you commit composition with specific string, specify commit string with its argument:
TIP.commitCompositionWith("foo-BAR-buzz");
When you cancel composition, just do this:
TIP.cancelComposition();
When you dispatch keydown event (and one or more keypress events), just do this:
var keyEvent = new KeyboardEvent("", // type attribute value should be empty. { key: "Enter", // Required. code: "Enter", // Optional. keyCode: KeyboardEvent.DOM_VK_RETURN, // Required if printable key, but optional if non-printable key. location: KeyboardEvent.DOM_VK_STANDARD, // Optional, may be computed from code attribute value. repeat: false, // Optional. }); // The other attributes are always ignored. var doDefault = TIP.keydown(keyEvent);
When you dispatch keyup event, just do this:
var keyEvent = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); var doDefault = TIP.keyup(keyEvent);
startComposition()
, flushPendingComposition()
, commitComposition()
, commitCompositionWith()
, and cancelComposition()
can take a KeyboardEvent which causes modifying the composition state. Specifying a keyboard event is strongly recommended if the TextInputProcessor
emulates input from keyboard. For example:
TIP.setPendingCompositionString("a"); TIP.appendClauseToPendingComposition("a".length, TIP.ATTR_RAW_CLAUSE); TIP.setCaretPosition("a".length); // This means that the first composition character is inputted by a keypress of "a" key. var AKeyEvent = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }); TIP.flushPendingComposition(AKeyEvent); // Pressing shift key for next input. var shiftKeyEvent = new KeyboardEvent("", { key: "Shift", code: "ShiftLeft", keyCode: KeyboardEvent.DOM_VK_SHIFT }); TIP.keydown(shiftKeyEvent); TIP.setPendingCompositionString("aB"); TIP.appendClauseToPendingComposition("aB".length, TIP.ATTR_RAW_CLAUSE); TIP.setCaretPosition("aB".length); // This means that the second composition character is inputted by a keypress of "b" key during left shift key is down. var BKeyEvent = new KeyboardEvent("", { key: "b", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B }); TIP.flushPendingComposition(BKeyEvent); // Releasing shift key TIP.keyup(shiftKeyEvent); TIP.setPendingCompositionString("AB"); TIP.appendClauseToPendingComposition("AB".length, TIP.ATTR_SELECTED_CLAUSE); TIP.setCaretPosition("AB".length); // This means that the composition string is converted by a keypress of "Convert" key. var convertKeyEvent = new KeyboardEvent("", { key: "Convert", code: "Convert" }); TIP.flushPendingComposition(convertKeyEvent); // This means that the composition is committed by a keypress of "Enter" key. var enterKeyEvent = new KeyboardEvent("", { key: "Enter", code: "Enter" }); TIP.commitComposition(enterKeyEvent);
Note that specifying keyboard event may not be dispatched during composition due to conforming to the specification of DOM Level 3 Events. However, it's the best for every TextInputProcessor user to specify KeyboardEvent every time because when Gecko will change the behavior of keyboard events during composition, TextInputProcessor must not need to change for the new behavior in most cases.
void appendClauseToPendingComposition(in unsigned long aLength, in unsigned long aAttribute); |
boolean beginInputTransaction(in nsIDOMWindow aWindow, in nsITextInputProcessorCallback aCallback); |
boolean beginInputTransactionForTests(in nsIDOMWindow aWindow, [optional] in nsITextInputProcessorCallback aCallback); |
void cancelComposition([optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags); |
boolean commitComposition([optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags); |
boolean commitCompositionWith(in DOMString aCommitString, [optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags); |
boolean flushPendingComposition([optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags); |
boolean getModifierState(in DOMString aModifierKeyName); |
boolean keydown([optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags); |
boolean keyup([optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags); |
void setCaretInPendingComposition(in unsigned long aOffset); |
void setPendingCompositionString(in DOMString aString); |
void shareModifierStateOf(in nsITextInputProcessor aOther); |
boolean startComposition ([optional] in nsIDOMKeyEvent aDOMKeyEvent, [optional] in unsigned long aKeyFlags ); |
Attribute | Type | Description |
---|---|---|
hasComposition | boolean | Whether the instance has composition or not. true if it has composition. Read only. |
Constant | Value | Description |
---|---|---|
ATTR_RAW_CLAUSE |
0x02 |
A clause attribute. This means that the clause is not converted, i.e., raw text of user input. |
ATTR_RAW_SELECTED_CLAUSE |
0x03 |
A clause attribute but this is typically not used. This means that the clause is not converted but selected to convert or modify. |
ATTR_CONVERTED_CLAUSE |
0x04 |
A clause attribute. This means that the clause is already converted. |
ATTR_SELECTED_CLAUSE |
0x05 |
A clause attribute. This means that the clause is already converted and is selected to convert. |
KEY_DEFAULT_PREVENTED |
0x00000001 |
One of aKeyFlags . When this is specified, defaultPrevented attribute of dispatching keyboard events true. This is not useful in usual cases. This may be useful for automated tests. |
KEY_NON_PRINTABLE_KEY |
0x00000002 |
One of aKeyFlags . When you attempt to dispatch a non-printable key's events, it's recommented this be specified. If the key attribute value is not a registered key name, this flag causes throwing an exception. Therefore, this can prevent non-printable key events to cause dispatching as printable keyboard events and you can detect the registered key name change from the thrown exception. |
KEY_FORCE_PRINTABLE_KEY |
0x00000004 |
One of aKeyFlags . When you attempt to dispatch a printable key's event whose key attribute value (i.e., inputting string) will match with a registered key name, this flag makes dispatched keyboard events printable keyboard events forcibly. For example, if a key causes inputting "Enter" as text, this flag is necessary. If this flag is not specified in such case, non-printable keyboard events will be dispatched. |
KEY_KEEP_KEY_LOCATION_STANDARD |
0x00000008 |
One of aKeyFlags . The location attribute value of dispatching keyboard events is computed automatically if the attribute isn't initialized or initialized with 0. For the latter case, you may not want TextInputProcessor to compute location attribute value. In this case, you can suppress the computation with this flag. If this is specified with non-zero location attribute value, this causes throwing an exception since it doesn't make sense. |
KEY_KEEP_KEYCODE_ZERO |
0x00000010 |
One of aKeyFlags . The keyCode attribute value of dispatching printable keyboard events is computed from key value automatically if the attribute isn't initialized or initialized with 0. For the latter case, you may not want TextInputProcessor to compute keyCode attribute value. In this case, you can suppress the computation with this flag. If this is specified with non-zero keyCode attribute value, this causes throwing an excepction since it doesn't make sense. |
KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT |
0x00000020 |
One of aKeyFlags . TextInputProcessor manages modifier state with every modifier key events. In other words, when you need to specify modifier key state of dispatching keyboard events, you need to dispatch modifier key events before and after that. However, especially for keeping compatibility with legacy API, you may want to modify only the modifier state of a TextInputProcessor instance. In this case, you can suppress TextInputProcessor dispatches modifier key events with this flag. If this is specified for non-modifier key, it causes throwing an exception since it doesn't make sense. |
Appends a clause to the pending composition string which is set by setPendingCompositionString(). Sum of aLength must be same as the length of aString
of setPendingCompositionString().
void appendClauseToPendingComposition(in unsigned long aLength, in unsigned long aAttribute);
ATTR_*
constants.This must be called before starting composition every time. This tries to get the rights to create composition on the window. If no other instance is composing on the window, this associates the instance with the window and registers the callback to the instance.
boolean beginInputTransaction(in nsIDOMWindow aWindow, in nsITextInputProcessorCallback aCallback);
nsITextInputProcessorCallback
for the details.Returns true if the instance could create composition. Otherwise, false.
This must be called before starting composition for automated tests every time. This tries to get the rights to create composition on the window. If no other instance is composing on the window, this associates the instance with the window and registers the callback to the instance if it's specified.
boolean beginInputTransactionForTests(in nsIDOMWindow aWindow, in nsITextInputProcessorCallback aCallback Optional);
nsITextInputProcessorCallback
for the details.Returns true if the instance could create composition. Otherwise, false.
Cancels the composition which was created by the instance. If you call this when there is no composition, this throws an exception.
void cancelComposition(in nsIDOMKeyEvent aDOMKeyEvent Optional, in unsigned long aKeyFlags Optional);
KEY_*
constants. If this is not specified, the value is assumed as 0.Commits the composition with the last composition string. If you call this when there is no composition, this throws an exception.
void commitComposition(in nsIDOMKeyEvent aDOMKeyEvent Optional, in unsigned long aKeyFlags Optional);
KEY_*
constants. If this is not specified, the value is assumed as 0.Commits the composition with the specified string. If you call this when there is no composition, this starts composition automatically and commits the composition with specified string immediately.
boolean commitCompositionWith(in DOMString aCommitString, in nsIDOMKeyEvent aDOMKeyEvent Optional, in unsigned long aKeyFlags aOptional);
KEY_*
constants. If this is not specified, the value is assumed as 0.If this couldn't insert the string when there is no composition (e.g., compositionstart
is consumed by the web contents), it returns false. The other cases return true.
Flushes the pending composition which are set by setPendingCompositionString(), appendClauseToPendingComposition(), and setCaretInPendingComposition() on the focused editor.
If appendClauseToPendingComposition() and/or setCaretInPendingComposition() are not called properly, e.g., sum of aLength of calls of appendClauseToPendingComposition() is not same as composition string set by setPendingCompositionString(), this throws an exception.
After a call of this, the pending composition string information is cleared. Therefore, you can set new composition string continuously.
boolean flushPendingComposition(in nsIDOMKeyEvent aDOMKeyEvent Optional, in unsigned long aKeyFlags Optional);
KEY_*
constants. If this is not specified, the value is assumed as 0.When there is no composition which was created by the instance and if compositionstart
is consumed by web contents, i.e., failed to start new composition, this returns false. Otherwise, it returns true.
Getting modifier state. This is similar to KeyboardEvent.getModifierState() [en-US]. The result is the modifier state of managed by the instance.
boolean getModifierState(in DOMString aModifierKeyName);
true if the modifier state is active. Otherwise, false.
Dispatches a keydown event and some keypress event when all of or some of them are necessary.
When this dispatches a normal modifier key event, the TextInputProcessor
instance activates the modifier state and stores its code
attribute value.
When this dispatches a lockable modifier key event, the TextInputProcessor
instance activates the modifier state or If the modifier state is activated with the same code
attribute value, the lockable modifier state is inactivated.
boolean keydown(in nsIDOMKeyEvent aDOMKeyEvent, in unsigned long aKeyFlags Optional);
KEY_*
constants. If this is not specified, the value is assumed as 0.true if neither dispatched keydown event nor keypress events are not consumed by a call of preventDefault()
.
Dispatches a keyup event when it's necessary.
When this dispatches a normal modifier key event, the TextInputProcessor
instance inactivates the modifier state if it was activated by same code
attribute.
boolean keyup(in nsIDOMKeyEvent aDOMKeyEvent, in unsigned long aKeyFlags Optional);
KEY_*
constants. If this is not specified, the value is assumed as 0.true if keyup event isn't consumed by a call of preventDefault()
.
Sets caret position in the composition string set by setPendingCompositionString(). This is optional, i.e., you may not call this before flushPendingComposition().
void setCaretInPendingComposition(in unsigned long aOffset);
Sets pending composition string. This must be called before every call of flushPendingComposition().
void setPendingCompositionString(in AString aString);
Shares modifier state of another instance. After sharing modifier state between two or more instances, modifying modifier state on of this affects other instances. Therefore, this is useful if you want to create TextInputProcessor
instance in every DOM window or something. See also Sharing modifier state during multiple instances.
void shareModifierStateOf(in nsITextInputProcessor aOther);
null
, the instance stops sharing modifier state with other instances and clears all modifier state.This starts composition explicitly. This is useful if your IME doesn't generate composing string but provides some UI to select commit string.
If the instance has a composition already, this throws an exception.
boolean startComposition(in nsIDOMKeyEvent aDOMKeyEvent Optional, in unsigned long aKeyFlags Optional);
KEY_*
constants. If this is not specified, the value is assumed as 0.When there is no composition which was created by the instance and if compositionstart
is consumed by web contents, i.e., failed to start new composition, this returns false. Otherwise, returns true.
This section describes how to create KeyboardEvent
for arguments of some methods of nsITextInputProcessor
.
You should/can specify following attribute values at creating a KeyboardEvent
instance. Other attributes are always ignored. Especially, please note that modifier key states such as shiftKey
are also ignored because each instance of nsITextInputProcessor
manages modifier state which is modified when keydown()
or keyup()
is called with modifier key event.
type
startComposition()
, flushPendingComposition()
, commitComposition()
, commitCompositionWith()
, or cancelComposition()
, you can specify "keydown"
or empty string. If "keydown"
is specified, the methods don't dispatch the "keyup"
event.key
"c"
, Ctrl+Shift+Cshould be "C"
, AltGr+C may be "©"
and then, Ctrl+AltGr+C should be "©"
.code
keyCode
keyCode
value are different on each web browser, you should use same rules as Gecko because your events are dispatched on Gecko. Basically, Gecko decides a keyCode
value for a printable key from ASCII character which is inputted with a far simpler modifier state. See "Printable keys in standard position" section of KeyboardEvent.keyCode for the strict rules of Gecko.keyCode
value is computed from key
value automatically if you don't initialize this value. But the computation doesn't emulate the mapping of native key event handling completely because it has some special cases depending on platform or selected keyboard layout. Therefore, if you need to emulate PC keyboard on specific platform, you may need to specify this explicitly.location
code
attribute value. The automatic computation can compute the best value for this.repeat
This section describes how to manage modifier state.
Each nsITextInputProcessor
instance manages modifier state internally and uses the state when it initializes a KeyboardEvent
instance before dispatching it.
When keydown()
is called with normal modifier key (i.e., not lockable like CapsLock) event, the instance stores the modifier key
and code
values. Stored modifier key information means that the modifier is active.
When keyup()
is called with normal modifier key event, the instance checks if there is a stored modifier key whose key
attribute and code
attribute are the same as the event. If the same modifier key is stored, the instance forgets the key. This means that the modifier is inactivated.
When keydown()
is called with lockable modifier key event, the instance checks if there is a stored modifier key whose key
attribute and code
attribute are the same as the event. If it's not stored, i.e., the modifier state is inactive, the instance stores the modifier key, otherwise forgets the key.
var leftShiftKey = new KeyboardEvent("", { key: "Shift", code: "ShiftLeft" }); var rightShiftKey = new KeyboardEvent("", { key: "Shift", code: "ShiftRight" }); var shiftKeyOnVirtualKeyboard = new KeyboardEvent("", { key: "Shift", code: "" }); TIP.keydown(leftShiftKey); // This activates Shift state. TIP.keydown(rightShiftKey); // This looks like not modifying Shift state. TIP.keyup(leftShiftKey); // This does NOT inactivate Shift state becaue right shift key is still pressed. TIP.keyup(rightShiftKey); // This inactivates Shift state. TIP.keydown(shiftKeyOnVirtualKeyboard); // This activates Shift state. TIP.keydown(leftShiftKey); TIP.keyup(shiftKeyOnVirtualKeyboard); // This also doesn't inactivate Shift state. TIP.keyup(leftShiftKey); // This inactivates Shift state.
var capsLockKey = new KeyboardEvent("", { key: "CapsLock", code: "CapsLock" }); var capsLockKeyOnVirtualKeyboard = new KeyboardEvent("", { key: "CapsLock", code: "" }); TIP.keydown(capsLockKey); // This activates CapsLock state. TIP.keyup(capsLockKey); // This doesn't inactivate CapsLock state. TIP.keyup(capsLockKeyOnVirtualKeyboard); // This also doesn't inactivate CapsLock state. TIP.keydown(capsLockKey); // This inactivates CapsLock state. TIP.keyup(capsLockKey); // This doesn't modify CapsLock state.
shareModifierStateOf()
allows to share modifier state between multiple instances:
var shiftKey = new KeyboardEvent("", { key: "Shift", code: "ShiftLeft" }); var ctrlKey = new KeyboardEvent("", { key: "Control", code: "ControlLeft" }); var altKey = new KeyboardEvent("", { key: "Alt", code: "AltLeft" }); TIP1.keydown(shiftKey); // TIP1's Shift state becomes active. TIP1.keyup(shiftKey); TIP2.keydown(ctrlKey); // TIP2's Control state becomes active. TIP2.keyup(ctrlKey); TIP3.shareModifierStateOf(TIP1); // TIP3 shares modifier state of TIP1. Therefore, TIP3's Shift state is active. TIP3.keydown(altKey); // TIP1 and TIP3's Alt state becomes active. TIP3.keyup(altKey); TIP3.shareModifierStateOf(TIP2); // TIP3 shares modifier state of TIP2. Therefore, only Control state is active. // Shift and Alt state are still active in TIP1. TIP3.shareModifierStateOf(null); // All TIP3's modifier state becomes deactive. // But TIP2's Control state is still active.