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.
Method overview
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 ); |
Attributes
Attribute | Type | Description |
---|---|---|
hasComposition | boolean | Whether the instance has composition or not. true if it has composition. Read only. |
Constants
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. |
Methods
appendClauseToPendingComposition()
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);
Parameters
- aLength
- The length of appending clause.
- aAttribute
- The attribute of appending clause. Must be one of
ATTR_*
constants.
beginInputTransaction()
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);
Parameters
- aWindow
- The DOM window which has focused editor. This must not be null.
- aCallback
- The callback object. This must not be null. See the document of
nsITextInputProcessorCallback
for the details.
Return value
Returns true if the instance could create composition. Otherwise, false.
beginInputTransactionForTests()
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);
Parameters
- aWindow
- The DOM window which has focused editor. This must not be null.
- aCallback
- The callback object. If this is not specified, requests to IMEs are handled automatically. See the document of
nsITextInputProcessorCallback
for the details.
Return value
Returns true if the instance could create composition. Otherwise, false.
cancelComposition()
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);
Parameters
- aDOMKeyEvent Optional
- When the canceling composition is caused by a key operation, this should be specified. If this is specified, keyboard events may be dispatched when Gecko thinks that they should be. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
commitComposition()
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);
Parameters
- aDOMKeyEvent Optional
- When the committing composition is caused by a key operation, this should be specified. If this is specified, keyboard events may be dispatched when Gecko thinks that they should be. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
commitCompositionWith()
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);
Parameters
- aCommitString
- The commit string.
- aDOMKeyEvent Optional
- When the committing composition is caused by a key operation, this should be specified. If this is specified, keyboard events may be dispatched when Gecko thinks that they should be. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
Return value
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.
flushPendingComposition()
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);
Parameters
- aDOMKeyEvent Optional
- When the modifying composition is caused by a key operation, this should be specified. If this is specified, keyboard events may be dispatched when Gecko thinks that they should be. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
Return value
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.
getModifierState()
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);
Parameters
- aModifierKeyName
- A modifier key name which is defined by DOM Level 3 KeyboardEvents key Values.
Return value
true if the modifier state is active. Otherwise, false.
keydown()
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);
Parameters
- aDOMKeyEvent
- The KeyboardEvent which is initialized with its constructor. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
Return value
true if neither dispatched keydown event nor keypress events are not consumed by a call of preventDefault()
.
keyup()
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);
Parameters
- aDOMKeyEvent
- The KeyboardEvent which is initialized with its constructor. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
Return value
true if keyup event isn't consumed by a call of preventDefault()
.
setCaretInPendingComposition()
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);
Parameters
- aOffset
- Caret offset in the composition string. This value must be between 0 and the length of aString of setPendingCompositionString().
setPendingCompositionString()
Sets pending composition string. This must be called before every call of flushPendingComposition().
void setPendingCompositionString(in AString aString);
Parameters
- aString
- New pending composition string.
shareModifierStateOf()
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);
Parameters
- aOther
- Another instance. If this is
null
, the instance stops sharing modifier state with other instances and clears all modifier state.
startComposition()
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);
Parameters
- aDOMKeyEvent Optional
- When the starting composition is caused by a key operation, this should be specified. If this is specified, keyboard events may be dispatched when Gecko thinks that they should be. See also How to create KeyboardEvent instance for nsITextInputProcessor.
- aKeyFlags Optional
- See
KEY_*
constants. If this is not specified, the value is assumed as 0.
Return value
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.
How to create KeyboardEvent instance for nsITextInputProcessor
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
- When you use the KeyboardEvent instance for a call of either keydown() or keyup(), this value should be empty string.
- Otherwise, when you use the KeyboardEvent instance for a call of
startComposition()
,flushPendingComposition()
,commitComposition()
,commitCompositionWith()
, orcancelComposition()
, you can specify"keydown"
or empty string. If"keydown"
is specified, the methods don't dispatch the"keyup"
event. key
- Required.
- When you dispatch printable key events, this value should be the text to be inputted. Even if the key event shouldn't cause text input actually, e.g., Ctrl key is pressed, this value should be the text when such modifiers causing suppressing text input are not pressed. For example, Ctrl+C should be
"c"
, Ctrl+Shift+Cshould be"C"
, AltGr+C may be"©"
and then, Ctrl+AltGr+C should be"©"
. - When you dispatch non-printable key events, this value should be one of the key names defined by DOM Level 3 KeyboardEvent key Values or registered by Gecko with "Moz" prefix.
code
- Optional, but should be specified when you emulate PC keyboard operation.
- The value should be one of the physical key names defined by DOM Level 3 KeyboardEvent code Values.
keyCode
- Optional, but should be specified when you dispatch printable key events.
- When you emulate PC keyboard, deciding the value may be complicated. Although the rules to decide the
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 akeyCode
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. - When you dispatch non-printable key events, preferred
keyCode
value is computed fromkey
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
- Optional. It's not recommended to specify this explicitly since this value should be linked with
code
attribute value. The automatic computation can compute the best value for this. repeat
- Optional. When you emulate auto-repeat caused by long keypress, you can specify this true.
Modifier state management
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.
Activating normal modifier state
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.
Inactivating normal modifier state
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.
Toggling lockable modifier state
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.
Examples of modifier state management
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.
Examples of lockable modifier state management
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.
Sharing modifier state during multiple instances
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.