gecko-dev/dom/interfaces/base/nsITextInputProcessor.idl

590 строки
28 KiB
Plaintext

/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIDOMKeyEvent;
interface nsIDOMWindow;
interface nsITextInputProcessorCallback;
/**
* An nsITextInputProcessor instance is associated with a top level widget which
* handles native IME. It's associated by calling beginInputTransaction() or
* beginInputTransactionForTests(). While an instance has composition, nobody
* can steal the rights to make composition on the top level widget. In other
* words, if another instance is composing on a top level widget, either
* beginInputTransaction() or beginInputTransactionForTests() returns false
* (i.e., not throws an exception).
*
* NOTE: See nsITextInputProcessorCallback.idl for examples of |callback| in
* following examples,
*
* Example #1 JS-IME can start composition like this:
*
* var TIP = Components.classes["@mozilla.org/text-input-processor;1"].
* createInstance(Components.interfaces.nsITextInputProcessor);
* if (!TIP.beginInputTransaction(window, callback)) {
* return; // You failed to get the rights to make composition
* }
* // Create a keyboard event if the following compositionc change is caused
* // by a key event.
* var keyEvent =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // Set new composition string first
* TIP.setPendingCompositionString("some-words-are-inputted");
* // Set clause information.
* TIP.appendClauseToPendingComposition(23, TIP.ATTR_RAW_CLAUSE);
* // Set caret position, this is optional.
* TIP.setCaretInPendingComposition(23);
* // Flush the pending composition
* if (!TIP.flushPendingComposition(keyEvent)) {
* // If it returns false, it fails to start composition.
* return;
* }
*
* Example #2 JS-IME can separate composition string to two or more clauses:
*
* // Create a keyboard event if the following compositionc change is caused
* // by a key event.
* var keyEvent =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // First, set composition string again
* TIP.setPendingCompositionString("some-words-are-inputted");
* // Then, if "are" is selected to convert, there are 3 clauses:
* TIP.appendClauseToPendingComposition(11, TIP.ATTR_CONVERTED_CLAUSE);
* TIP.appendClauseToPendingComposition(3, TIP.ATTR_SELECTED_CLAUSE);
* TIP.appendClauseToPendingComposition(9, TIP.ATTR_CONVERTED_CLAUSE);
* // Show caret at the beginning of the selected clause
* TIP.setCaretInPendingComposition(11);
* // Flush the pending composition. Note that if there is a composition,
* // flushPendingComposition() won't return false.
* TIP.flushPendingComposition(keyEvent);
*
* Example #3 JS-IME can commit composition with specific string with this:
*
* // Create a keyboard event if the following compositionc change is caused
* // by a key event.
* var keyEvent1 =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // First, there is a composition.
* TIP.setPendingCompositionString("some-words-directly-inputted");
* TIP.appendClauseToPendingComposition(28, TIP.ATTR_RAW_CLAUSE);
* TIP.flushPendingComposition(keyEvent1);
* // Create a keyboard event if the following commit composition is caused
* // by a key event.
* var keyEvent2 =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // This is useful when user selects a commit string from candidate list UI
* // which is provided by JS-IME.
* TIP.commitCompositionWith("selected-words-from-candidate-list", keyEvent2);
*
* Example #4 JS-IME can commit composition with the last composition string
* without specifying commit string:
*
* // Create a keyboard event if the following compositionc change is caused
* // by a key event.
* var keyEvent1 =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // First, there is a composition.
* TIP.setPendingCompositionString("some-words-will-be-commited");
* TIP.appendClauseToPendingComposition(27, TIP.ATTR_RAW_CLAUSE);
* TIP.flushPendingComposition(keyEvent1);
* // Create a keyboard event if the following commit is caused by a key
* // event.
* var keyEvent2 =
* new KeyboardEvent("", { key: "Enter", code: "Enter",
keyCode: KeyboardEvent.DOM_VK_RETURN });
* // This is useful when user just type Enter key.
* TIP.commitComposition(keyEvent2);
*
* Example #5 JS-IME can cancel composition with this:
*
* // Create a keyboard event if the following composition change is caused
* // by a key event.
* var keyEvent1 =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // First, there is a composition.
* TIP.setPendingCompositionString("some-words-will-be-canceled");
* TIP.appendClauseToPendingComposition(27, TIP.ATTR_RAW_CLAUSE);
* TIP.flushPendingComposition(keyEvent1);
* // Create a keyboard event if the following canceling composition is
* // caused by a key event.
* var keyEvent2 =
* new KeyboardEvent("", { key: "Escape", code: "Escape",
keyCode: KeyboardEvent.DOM_VK_ESCAPE });
* // This is useful when user doesn't want to commit the composition.
* // FYI: This is same as TIP.commitCompositionWith("") for now.
* TIP.cancelComposition(keyEvent2);
*
* Example #6 JS-IME can insert text only with commitCompositionWith():
*
* // Create a keyboard event if the following inserting text is caused by a
* // key event.
* var keyEvent1 =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* if (!TIP.beginInputTransaction(window, callback)) {
* return; // You failed to get the rights to make composition
* }
* TIP.commitCompositionWith("Some words", keyEvent1);
*
* Example #7 JS-IME can start composition explicitly:
*
* if (!TIP.beginInputTransaction(window, callback)) {
* return; // You failed to get the rights to make composition
* }
* // Create a keyboard event if the following starting composition is caused
* // by a key event.
* var keyEvent1 =
* new KeyboardEvent("", { key: "foo", code: "bar", keyCode: buzz });
* // If JS-IME don't want to show composing string in the focused editor,
* // JS-IME can dispatch only compositionstart event with this.
* if (!TIP.startComposition(keyEvent1)) {
* // Failed to start composition.
* return;
* }
* // And when user selects a result from UI of JS-IME, commit with it.
* // Then, the key event should be null.
* TIP.commitCompositionWith("selected-words");
*
* Example #8 JS-IME or JS-Keyboard should dispatch key events even during
* composition (non-printable key case):
*
* if (!TIP.beginInputTransaction(window, callback)) {
* return; // You failed to get the rights to dispatch key events
* }
*
* // You don't need to specify .keyCode value if it's non-printable key
* // because it can be computed from .key value.
* // If you specify non-zero value to .keyCode, it'll be used.
* var keyEvent = new KeyboardEvent("", { code: "Enter", key: "Enter" });
* if (TIP.keydown(keyEvent)) {
* // Handle its default action
* }
*
* // Even if keydown event was consumed, keyup event should be dispatched.
* if (TIP.keyup(keyEvent)) {
* // Handle its default action
* }
*
* Example #9 JS-IME or JS-Keyboard should dispatch key events even during
* composition (printable key case):
*
* if (!TIP.beginInputTransaction(window, callback)) {
* return; // You failed to get the rights to dispatch key events
* }
*
* // You need to specify .keyCode value if it's printable key.
* // The rules of .keyCode value is documented in MDN:
* // https://developer.mozilla.org/docs/Web/API/KeyboardEvent.keyCode
* //
* // #1 If the key location is DOM_KEY_LOCATION_NUMPAD and NumLock is
* // active, you should specify DOM_VK_NUMPAD[0-9], DOM_VK_MULTIPLY,
* // DOM_VK_ADD, DOM_VK_SEPARATOR, DOM_VK_SUBTRACT, DOM_VK_DECIMAL or
* // DOM_VK_DIVIDE.
* // #2 If the key is Spacebar, use DOM_VK_SPACE.
* //
* // Following rules are printable keys in DOM_KEY_LOCATION_STANDARD.
* // .keyCode value for a key shouldn't be changed by modifier states:
* // #1 If the key can input [0-9] with any modifier state (except
* // NumLock state), the value should be DOM_VK_[0-9].
* // #2 Otherwise, and if the key inputs an ASCII alphabet with no
* // active modifiers, use DOM_VK_[A-Z].
* // #3 Otherwise, and if the key inputs an ASCII alphabet with no
* // active modifiers except Shift key state, use DOM_VK_[A-Z] for
* // the shifted character. E.g., if a key causes non-alphabet
* // character such as "@" or a Unicode character without Shift key
* // but "a" is inputted when Shift key is pressed, the proper
* // keyCode is DOM_VK_A.
* // #4 Otherwise, and if the key inputs another ASCII character with
* // no modifier states, use a proper value for the character. E.g.,
* // if the key inputs "*" without Shift key state, it should be
* // DOM_VK_ASTERISK.
* // #5 Otherwise, and if the key inputs another ASCII character with
* // Shift key state, use a proper value for the character. E.g.,
* // if a key causes a Unicode character without Shift key but "&"
* // is inputted when Shift key is pressed, the proper keyCode is
* // DOM_VK_AMPERSAND.
* // See above document for the other cases.
* //
* // NOTE: If the software keyboard is 10-key like simple phone,
* // We don't have common rules to decide its .keyCode value.
* // Above rules should be used when the JS-Keyboard emulates PC
* // keyboard.
* // .key value should be inputting character by the key with current
* // modifier state.
* // .code value should be empty string if the JS-Keyboard isn't emulating
* // physical keyboard. Otherwise, use same value with physical keyboard's
* // same key.
* var keyEvent = new KeyboardEvent("", { code: "KeyA", key: "a",
* keyCode: KeyboardEvent.DOM_VK_A });
* if (TIP.keydown(keyEvent)) {
* // Handle its default action
* }
*
* // Even if keydown event was consumed, keyup event should be dispatched.
* if (TIP.keyup(keyEvent)) {
* // Handle its default action
* }
*
* Example #10 JS-Keyboard doesn't need to initialize modifier states at
* calling either keydown() or keyup().
*
* // Neither beginInputTransaction() nor beginInputTransactionForTests()
* // resets modifier state.
* if (!TIP.beginInputTransaction(window, callback)) {
* return; // You failed to get the rights to dispatch key events
* }
*
* var leftShift = new KeyboardEvent("", { code: "ShiftLeft", key: "Shift" });
*
* // This causes following key events will be shifted automatically.
* TIP.keydown(leftShift);
*
* var rightShift =
* new KeyboardEvent("", { code: "ShiftRight", key: "Shift" });
*
* TIP.keydown(rightShift);
*
* // keyup of one of shift key doesn't cause inactivating "Shift" state.
* TIP.keyup(rightShift);
*
* // This causes inactivating "Shift" state completely.
* TIP.keyup(leftShift);
*/
[scriptable, builtinclass, uuid(581f6619-76ed-478c-905d-b8e6553a714a)]
interface nsITextInputProcessor : nsISupports
{
/**
* Returns true if this instance was dispatched compositionstart but hasn't
* dispatched compositionend yet.
*/
readonly attribute boolean hasComposition;
/**
* When you create an instance, you must call beginInputTransaction() first
* except when you created the instance for automated tests.
*
* @param aWindow A DOM window. The instance will look for a top
* level widget from this.
* @param aCallback Callback interface which handles requests to
* IME and notifications to IME. This must not be
* null.
* @return If somebody uses internal text input service for a
* composition, this returns false. Otherwise, returns
* true. I.e., only your TIP can create composition
* when this returns true. If this returns false,
* your TIP should wait next chance.
*/
boolean beginInputTransaction(in nsIDOMWindow aWindow,
in nsITextInputProcessorCallback aCallback);
/**
* When you create an instance for automated test, you must call
* beginInputTransaction(), first. See beginInputTransaction() for more
* detail of this.
* Note that aCallback can be null. If it's null, nsITextInputProcessor
* implementation will handle them automatically.
*/
[optional_argc] boolean
beginInputTransactionForTests(
in nsIDOMWindow aWindow,
[optional] in nsITextInputProcessorCallback aCallback);
/**
* startComposition() dispatches compositionstart event explicitly.
* IME does NOT need to call this typically since compositionstart event
* is automatically dispatched by sendPendingComposition() if
* compositionstart event hasn't been dispatched yet. If this is called
* when compositionstart has already been dispatched, this throws an
* exception.
*
* @param aKeyboardEvent Key event which causes starting composition.
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* @param aKeyFlags See KEY_* constants.
* @return Returns true if composition starts normally.
* Otherwise, returns false because it might be
* canceled by the web application.
*/
[optional_argc]
boolean startComposition([optional] in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
/**
* Set new composition string. Pending composition will be flushed by
* a call of flushPendingComposition(). However, if the new composition
* string isn't empty, you need to call appendClauseToPendingComposition() to
* fill all characters of aString with one or more clauses before flushing.
* Note that if you need to commit or cancel composition, use
* commitComposition(), commitCompositionWith() or cancelComposition().
*/
void setPendingCompositionString(in DOMString aString);
// ATTR_RAW_CLAUSE means that the clause hasn't been selected nor converted
// yet.
const unsigned long ATTR_RAW_CLAUSE = 0x02;
// ATTR_SELECTED_RAW_CLAUSE means that the clause hasn't been converted yet
// but is selected for converting to the other string.
const unsigned long ATTR_SELECTED_RAW_CLAUSE = 0x03;
// ATTR_CONVERTED_CLAUSE means that the clause has already been converted but
// is not selected. This does NOT mean that this clause isn't modifiable.
const unsigned long ATTR_CONVERTED_CLAUSE = 0x04;
// ATTR_SELECTED_CLAUSE means that the clause has already been converted and
// is selected. In other words, the clause is being converted.
const unsigned long ATTR_SELECTED_CLAUSE = 0x05;
/**
* Append a clause to the pending composition.
*
* If you need to fill the pending composition string with a clause, you
* should call this once. For example:
* appendClauseToPendingComposition(compositionString.length,
* ATTR_RAW_CLAUSE);
* is enough. If you need to separate the pending composition string to
* multiple clauses, you need to call this multiple times. For example,
* if your pending composition string has three clauses and the second clause
* is being converted:
* appendClauseToPendingComposition(firstClauseLength,
* ATTR_CONVERTED_CLAUSE);
* appendClauseToPendingComposition(secondClauseLength,
* ATTR_SELECTED_CLAUSE);
* appendClauseToPendingComposition(thirdClauseLength,
* ATTR_CONVERTED_CLAUSE);
* Note that if sum of aLength mismatches length of the pending composition
* string, flushPendingComposition() will throw an exception. I.e.,
* |firstClauseLength + secondClauseLength + thirdClauseLength| must be
* same as the length of pending composition string.
*
* TODO: Should be able to specify custom clause style.
*
* @param aLength Length of the clause.
* @param aAttribute One of ATTR_* constants.
*/
void appendClauseToPendingComposition(in unsigned long aLength,
in unsigned long aAttribute);
/**
* Set caret offset in the pending composition string. If you don't need to
* show a caret, you don't need to call this.
*
* @param aOffset Caret offset in the pending composition string.
* This must be between 0 and length of the pending
* composition string.
*/
void setCaretInPendingComposition(in unsigned long aOffset);
/**
* flushPendingComposition() must be called after
* setPendingCompositionString() and appendClauseToPendingComposition()
* (setCaretInPendingComposition() is optional) are called.
*
* Note that compositionstart will be automatically dispatched if this is
* called when there is no composition.
*
* Note that if sum of lengths of appended clauses are not same as composition
* string or caret offset is larger than the composition string length, this
* throws an exception.
*
* @param aKeyboardEvent Key event which causes the composition string.
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* @param aKeyFlags See KEY_* constants.
* @return Returns true if there is a composition already or
* starting composition automatically.
* Otherwise, i.e., if it cannot start composition
* automatically, e.g., canceled by web apps, returns
* false.
*/
[optional_argc]
boolean flushPendingComposition(
[optional] in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
/**
* commitComposition() will commit composition with the last composition
* string. If there is no composition, this will throw an exception.
*
* @param aKeyboardEvent Key event which causes the commit composition.
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* @param aKeyFlags See KEY_* constants.
*/
[optional_argc]
void commitComposition([optional] in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
/**
* commitCompositionWith() will commit composition with the specific string.
* If there is no composition, this will start composition and commit it
* with the specified string.
*
* @param aCommitString The string to be committed.
* @param aKeyboardEvent Key event which causes the commit composition.
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* @param aKeyFlags See KEY_* constants.
* @return Returns true if there is a composition already or
* starting composition automatically.
* Otherwise, i.e., if it cannot start composition
* automatically, e.g., canceled by web apps, returns
* false.
*/
[optional_argc]
boolean commitCompositionWith(in DOMString aCommitString,
[optional] in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
/**
* cancelComposition() will cancel composition. This is for now the same as
* calling commitComposition(""). However, in the future, this might work
* better. If your IME needs to cancel composition, use this instead of
* commitComposition().
*
* Note that if you tries to cancel composition when there is no composition,
* this throws an exception.
*
* @param aKeyboardEvent Key event which causes the canceling composition.
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* @param aKeyFlags See KEY_* constants.
*/
[optional_argc]
void cancelComposition([optional] in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
// Specifying KEY_DEFAULT_PREVENTED can dispatch key events whose
// defaultPrevented are true. Note that if this is specified, keypress event
// won't be fired.
const unsigned long KEY_DEFAULT_PREVENTED = 0x00000001;
// If KEY_NON_PRINTABLE_KEY is specified and the .key value isn't valid
// key name, the methods will throws an exception. In other words, this
// flag prevents to dispatch key events with wrong key values and to cause
// such key events input the key values as text.
const unsigned long KEY_NON_PRINTABLE_KEY = 0x00000002;
// If KEY_FORCE_PRINTABLE_KEY is specified and even if the .key value is a
// registered key name, it's treated as inputting text value.
const unsigned long KEY_FORCE_PRINTABLE_KEY = 0x00000004;
// If KEY_KEEP_KEY_LOCATION_STANDARD is specified when its .location is not
// initialized or initialized with 0, the value isn't computed with .code
// value. Note that if .location is initialized with non-zero value,
// this flag causes throwing an exception.
// NOTE: This is not recommended to use except for tests.
const unsigned long KEY_KEEP_KEY_LOCATION_STANDARD = 0x00000008;
// If KEY_KEEP_KEYCODE_ZERO is specified when its .keyCode is not initialized
// or initialized with 0, the value isn't computed with .key value when it
// represents non-printable key. Note that if .keyCode is initialized with
// non-zero value, this flag causes throwing an exception.
const unsigned long KEY_KEEP_KEYCODE_ZERO = 0x00000010;
// If KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT is specified when the key event is
// a modifier key's, keydown() and keyup() only modifies its modifier state
// without dispatching key events. This is useful for testing odd behavior
// or emulating legacy API behavior.
const unsigned long KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT = 0x00000020;
// These values can be used to do bitwise operation with the return value of
// the keydown() method.
const unsigned long KEYEVENT_NOT_CONSUMED = 0x00000000;
const unsigned long KEYDOWN_IS_CONSUMED = 0x00000001;
const unsigned long KEYPRESS_IS_CONSUMED = 0x00000002;
/**
* keydown() may dispatch a keydown event and some keypress events if
* preceding keydown event isn't consumed and they are necessary.
* Note that even if this is called during composition, key events may not
* be dispatched. In this case, this returns false.
*
* You should initialize at least .key value and .code value of the event.
* Additionally, if you try to emulate a printable key, .keyCode value should
* be specified if there is proper key value. See the comment of above
* example how to decide .keyCode value of a printable key. On the other
* hand, .keyCode value is automatically computed when you try to emulate
* non-printable key. However, if you try to emulate physical keyboard of
* desktop platform, you need to specify proper value explicitly because
* the mapping table of this API isn't enough to emulate the behavior of
* Gecko for desktop platforms.
*
* NOTE: Even if this has composition, JS-Keyboard should call keydown() and
* keyup(). Although, with the default preferences and normal
* conditions, DOM key events won't be fired during composition.
* However, they MAY be dispatched for some reasons, e.g., the web
* content listens only key events, or if the standard DOM event spec
* will be changed in the future.
*
* @param aKeyboardEvent Must be a keyboard event which should be dispatched
* as a keydown event and keypress events.
* #1 Note that you don't need to set charCode value
* because it's computed from its key value.
* #2 If code value is set properly and location value
* isn't specified (i.e., 0), the location value will
* be guessed from the code value.
* #3 Non-defined code names are not allowed. If your
* key isn't registered, file a bug. If your key isn't
* defined by any standards, use "" (empty string).
* #4 .keyCode is guessed from .key value if the key
* name is registered and .keyCode isn't initialized.
* #5 modifier key states, e.g., .shiftKey, are
* ignored. Instead, modifier states are managed by
* each instance and set automatically.
* @param aKeyFlags Special flags. The values can be some of KEY_*
* constants.
* @return KEYEVENT_NOT_CONSUMED, if the keydown event nor
* the following keypress event(s) are consumed.
* KEYDOWN_IS_CONSUMED, if the keydown event is
* consumed. No keypress event will be dispatched in
* this case.
* KEYPRESS_IS_CONSUMED, if the keypress event(s) is
* consumed when dispatched.
* Note that keypress event is always consumed by
* native code for the printable keys (indicating the
* default action has been taken).
*/
[optional_argc]
unsigned long keydown(in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
/**
* Similar to keydown(), but this dispatches only a keyup event.
*/
[optional_argc]
boolean keyup(in nsIDOMKeyEvent aKeyboardEvent,
[optional] in unsigned long aKeyFlags);
/**
* getModifierState() returns modifier state managed by this instance.
*
* @param aModifier One of modifier key names. This doesn't support
* virtual modifiers like "Accel".
* @return true if the modifier key is active. Otherwise,
* false.
*/
boolean getModifierState(in DOMString aModifierKey);
/**
* shareModifierStateOf() makes the instance shares modifier state of
* another instance. When this is called, the instance refers the modifier
* state of another instance. After that, changes to either this and the
* other instance's modifier state is synchronized.
*
* @param aOther Another instance which will be referred by the
* instance. If this is null, the instance restarts
* to manage modifier state independently.
*/
void shareModifierStateOf(in nsITextInputProcessor aOther);
};
%{C++
#define TEXT_INPUT_PROCESSOR_CID \
{ 0xcaaab47f, 0x1e31, 0x478e, \
{ 0x89, 0x19, 0x97, 0x09, 0x04, 0xe9, 0xcb, 0x72 } }
#define TEXT_INPUT_PROCESSOR_CONTRACTID \
"@mozilla.org/text-input-processor;1"
%}