зеркало из https://github.com/mozilla/gecko-dev.git
1092 строки
49 KiB
ReStructuredText
1092 строки
49 KiB
ReStructuredText
==================
|
|
IME handling guide
|
|
==================
|
|
|
|
This document explains how Gecko handles IME.
|
|
|
|
Introduction
|
|
============
|
|
|
|
IME is an abbreviation of Input Method Editor. This is a technical term from
|
|
Windows but these days, this is used on other platforms as well.
|
|
|
|
IME is a helper application of a user's text input. It handles native key
|
|
events before or after focused application (depending on the platform) and
|
|
creates a composition string (a.k.a. preedit string), suggests a list of what
|
|
the user attempts to input, commits composition string as a selected item off
|
|
the list and commits composition string without any conversion. IME is used by
|
|
Chinese, Japanese, Korean and Taiwan users for inputting Chinese characters
|
|
because the number of them is beyond thousands and cannot be input from the
|
|
keyboard directly. However, especially on mobile devices nowadays, IME is also
|
|
used for inputting Latin languages like autocomplete. Additionally, IME may be
|
|
used for handwriting systems or speech input systems on some platforms.
|
|
|
|
If IME is available on focused elements, we call that state "enabled". If IME
|
|
is not fully available(i.e., user cannot enable IME), we call this state
|
|
"disabled".
|
|
|
|
If IME is enabled but users use direct input mode (e.g., for inputting Latin
|
|
characters), we call it "IME is closed". Otherwise, we call it "IME is open".
|
|
(FYI: "open" is also called "active" or "turned on". "closed" is also called
|
|
"inactive" or "turned off")
|
|
|
|
So, this document is useful when you're try to fix a bug for text input in
|
|
Gecko.
|
|
|
|
|
|
Composition string and clauses
|
|
==============================
|
|
|
|
Typical Japanese IME can input two or more words into a composition string.
|
|
When a user converts from Hiragana characters to Chinese characters the
|
|
composition string, Japanese IME separates the composition string into multiple
|
|
clauses. For example, if a user types "watasinonamaehanakanodesu", it's
|
|
converted to Hiragana characters, "わたしのなまえはなかのです", automatically (In
|
|
the following screenshots, the composition string has a wavy underline and the
|
|
only one clause is called "raw input clause").
|
|
|
|
.. image:: inputting_composition_string.png
|
|
:alt: Screenshot of raw composition string which is inputting Roman
|
|
character mode of MS-IME (Japanese)
|
|
|
|
.. image:: raw_composition_string.png
|
|
:alt: Screenshot of raw composition string whose all characters are Hiragana
|
|
character (MS-IME, Japanese)
|
|
|
|
When a user presses ``Convert`` key, Japanese IME separates the composition
|
|
string as "わたしの" (my), "なまえは" (name is) and "なかのです" (Nakano). Then,
|
|
converts each clause with Chinese characters: "私の", "名前は" and "中野です" (In
|
|
the following screenshot each clause is underlined and not connected
|
|
adjacently. These clauses are called "converted clause").
|
|
|
|
.. image:: converted_composition_string.png
|
|
:alt: Screenshot of converted composition string (MS-IME, Japanese)
|
|
|
|
If one or more clauses were not converted as expected, the user can choose one
|
|
of the clauses with Arrow keys and look for the expected result form the list
|
|
in the drop down menu (In the following screenshot, the clause with the thicker
|
|
underline is called "selected clause").
|
|
|
|
.. image:: candidatewindow.png
|
|
:alt: Screenshot of candidate window of MS-IME (Japanese) which converts the
|
|
selected clause
|
|
|
|
Basically, composition string and each clause style is rendered by Gecko. And
|
|
the drop down menu is created by IME.
|
|
|
|
Each clause is represented with selection in the editor. From chrome script,
|
|
you can check it with ``nsISelectionController``. In native code, you can
|
|
access it with either ``nsISelectionController`` or ``mozilla::SelectionType``
|
|
(the latter is recommended because of type safer). And editor sets these IME
|
|
selections from ``mozilla::TextRangeType`` which are sent by
|
|
``mozilla::WidgetCompositionEvent`` as ``mozilla::TextRangeArray``. The
|
|
following table explains the mapping between them.
|
|
|
|
.. table:: Selection types of each clause of composition string or caret
|
|
|
|
+------------------------------------------------------------+---------------------------------------+-------------------------+-------------------------+
|
|
| |`nsISelectionController`_ |`mozilla::SelectionType`_|`mozilla::TextRangeType`_|
|
|
+============================================================+=======================================+=========================+=========================+
|
|
|Caret |``SELECTION_NORMAL`` |``eNormal`` |``eCaret`` |
|
|
+------------------------------------------------------------+---------------------------------------+-------------------------+-------------------------+
|
|
|Raw text typed by the user |``SELECTION_IME_RAW_INPUT`` |``eIMERawClause`` |``eRawClause`` |
|
|
+------------------------------------------------------------+---------------------------------------+-------------------------+-------------------------+
|
|
|Selected clause of raw text typed by the user |``SELECTION_IME_SELECTEDRAWTEXT`` |``eIMESelectedRawClause``|``eSelectedRawClause`` |
|
|
+------------------------------------------------------------+---------------------------------------+-------------------------+-------------------------+
|
|
|Converted clause by IME |``SELECTION_IME_CONVERTEDTEXT`` |``eIMEConvertedClause`` |``eConvertedClause`` |
|
|
+------------------------------------------------------------+---------------------------------------+-------------------------+-------------------------+
|
|
|Selected clause by the user or IME and also converted by IME|``SELECTION_IME_SELECTEDCONVERTEDTEXT``|``eIMESelectedClause`` |``eSelectedClause`` |
|
|
+------------------------------------------------------------+---------------------------------------+-------------------------+-------------------------+
|
|
|
|
Note that typically, "Selected clause of raw text typed by the user" isn't used
|
|
because when composition string is already separated to multiple clauses, that
|
|
means that the composition string has already been converted by IME at least
|
|
once.
|
|
|
|
.. _nsISelectionController: https://searchfox.org/mozilla-central/source/dom/base/nsISelectionController.idl
|
|
.. _mozilla::SelectionType: https://searchfox.org/mozilla-central/source/dom/base/nsISelectionController.idl
|
|
.. _mozilla::TextRangeType: https://searchfox.org/mozilla-central/source/widget/TextRange.h
|
|
|
|
Modules handling IME composition
|
|
================================
|
|
|
|
widget
|
|
------
|
|
|
|
Each widget handles native IME events and dispatches ``WidgetCompositionEvent``
|
|
with ``mozilla::widget::TextEventDispatcher`` to represent the behavior of IME
|
|
in the focused editor.
|
|
|
|
This is the only module that depends on the users platform. See also
|
|
`Native IME handlers`_ section for the detail of each platform's
|
|
implementation.
|
|
|
|
.. note::
|
|
|
|
Android widget still does not use ``TextEventDispatcher`` to dispatch
|
|
``WidgetCompositionEvents``, see
|
|
`bug 1137567 <https://bugzilla.mozilla.org/show_bug.cgi?id=1137567>`__.
|
|
|
|
mozilla::widget::TextEventDispatcher
|
|
------------------------------------
|
|
|
|
This class is used by native IME handler(s) on each platform. This capsules the
|
|
logic to dispatch ``WidgetCompositionEvent`` and ``WidgetKeyboardEvent`` for
|
|
making the behavior on each platform exactly same. For example, if
|
|
``WidgetKeyboardEvent`` should be dispatched when there is a composition is
|
|
managed by this class in XP level. First of use, native IME handlers get the
|
|
rights to use ``TextEventDispatcher`` with a call of
|
|
``BeginNativeInputTransaction()``. Then, ``StartComposition()``,
|
|
``SetPendingComposition()``, ``FlushPendingComposition()``,
|
|
``CommitComposition()``, etc. are available if
|
|
``BeginNativeInputTransaction()`` return true. These methods automatically
|
|
manage composition state and dispatch ``WidgetCompositionEvent`` properly.
|
|
|
|
This is also used by ``mozilla::TextInputProcessor`` which can emulates (or
|
|
implements) IME with chrome script. So, native IME handlers using this class
|
|
means that the dispatching part is also tested by automated tests.
|
|
|
|
mozilla::WidgetCompositionEvent
|
|
-------------------------------
|
|
|
|
Internally, ``WidgetCompositionEvent`` represents native IME behavior. Its
|
|
message is one of following values:
|
|
|
|
eCompositionStart
|
|
^^^^^^^^^^^^^^^^^
|
|
|
|
This is dispatched at starting a composition. This represents a DOM
|
|
``compositionstart`` event. The mData value is a selected string at dispatching
|
|
the DOM event and it's automatically set by ``TextComposition``.
|
|
|
|
eCompositionUpdate
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
This is dispatched by ``TextComposition`` when an ``eCompositionChange`` will
|
|
change the composition string. This represents a DOM ``compositionupdate``
|
|
event.
|
|
|
|
eCompositionEnd
|
|
^^^^^^^^^^^^^^^
|
|
|
|
This is dispatched by ``TextComposition`` when an ``eCompositionCommitAsIs`` or
|
|
``eCompositionCommit`` event is dispatched. This represents a DOM
|
|
``compositionend`` event.
|
|
|
|
eCompositionChange
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
This is used internally only. This is dispatched at modifying a composition
|
|
string, committing a composition, changing caret position and/or changing
|
|
ranges of clauses. This represents a DOM text event which is not in any
|
|
standards. ``mRanges`` should not be empty only with this message.
|
|
|
|
eCompositionCommitAsIs
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
This is used internally only. This is dispatched when a composition is
|
|
committed with the string. The ``mData`` value should be always be an empty
|
|
string. This causes a DOM text event without clause information and a DOM
|
|
``compositionend`` event.
|
|
|
|
eCompositionCommit
|
|
^^^^^^^^^^^^^^^^^^
|
|
|
|
This is used internally only. This is dispatched when a composition is
|
|
committed with specific string. The ``mData`` value is the commit string. This
|
|
causes a DOM text event without clause information and a DOM ``compositionend``
|
|
event.
|
|
|
|
.. table:: Table of event messages
|
|
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
| |meaning of mData |who sets ``mData``? |``mRanges`` |representing DOM event|
|
|
+==========================+===========================================+===============================+=======================+======================+
|
|
|``eCompositionStart`` |selected string before starting composition|``TextComposition`` |``nullptr`` |``compositionstart`` |
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
|``eCompositionUpdate`` |new composition string |``TextComposition`` |``nullptr`` |``compositionupdate`` |
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
|``eCompositionEnd`` |commit string |``TextComposition`` |``nullptr`` |``compositionend`` |
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
|``eCompositionChange`` |new composition string |widget (or ``TextComposition``)|must not be ``nullptr``|``text`` |
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
|``eCompositionCommitAsIs``|N/A (must be empty) |nobody |``nullptr`` |None |
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
|``eCompositionCommit`` |commit string |widget (or ``TextComposition``)|``nullptr`` |None |
|
|
+--------------------------+-------------------------------------------+-------------------------------+-----------------------+----------------------+
|
|
|
|
PresShell
|
|
---------
|
|
|
|
``PresShell`` receives the widget events and decides an event target from
|
|
focused document and element. Then, it sends the events and the event target to
|
|
``IMEStateManager``.
|
|
|
|
mozilla::IMEStateManager
|
|
------------------------
|
|
|
|
``IMEStateManager`` looks for a ``TextComposition`` instance whose native IME
|
|
context is same as the widget' which dispatches the widget event. If there is
|
|
no proper ``TextComposition`` instance, it creates the instance. And it sends
|
|
the event to the ``TextComposition`` instance.
|
|
|
|
Note that all instances of ``TextComposition`` are managed by
|
|
``IMEStateManager``. When an instance is created, it's registered to the list.
|
|
When composition completely ends, it's unregistered from the list (and released
|
|
automatically).
|
|
|
|
mozilla::TextComposition
|
|
------------------------
|
|
|
|
``TextComposition`` manages a composition and dispatches DOM
|
|
``compositionupdate`` events.
|
|
|
|
When this receives an ``eCompositionChange``, ``eCompositionCommit`` or
|
|
``eCompositionCommitAsIs`` event, it dispatches the event to the stored node
|
|
which was the event target of ``eCompositionStart`` event. Therefore, this
|
|
class guarantees that all composition events for a composition are fired on
|
|
same element.
|
|
|
|
When this receives ``eCompositionChange`` or ``eCompositionCommit``, this
|
|
checks if new composition string (or committing string) is different from the
|
|
last data stored by the ``TextComposition``. If the composition event is
|
|
changing the composition string, the ``TextComposition`` instance dispatches
|
|
``WidgetCompositionEvent`` with ``eCompositionUpdate`` into the DOM tree
|
|
directly and modifies the last data. The ``eCompositionUpdate`` event will
|
|
cause a DOM ``compositionupdate`` event.
|
|
|
|
When this receives ``eCompositionCommitAsIs`` or ``eCompositionCommit``, this
|
|
dispatches an ``eCompositionEnd`` event which will cause a DOM
|
|
``compositionend`` event after dispatching ``eCompositionUpdate`` event and/or
|
|
``eCompositionChange`` event if necessary.
|
|
|
|
One of the other important jobs of this is, when a focused editor handles a
|
|
dispatched ``eCompositionChange`` event, this modifies the stored composition
|
|
string and its clause information. The editor refers the stored information for
|
|
creating or modifying a text node representing a composition string.
|
|
|
|
And before dispatching ``eComposition*`` events, this class removes ASCII
|
|
control characters from dispatching composition event's data in the default
|
|
settings. Although, this can be disabled with
|
|
``"dom.compositionevent.allow_control_characters"`` pref.
|
|
|
|
Finally, this class guarantees that requesting to commit or cancel current
|
|
composition to IME is perefored synchronously. See
|
|
`Forcibly committing composition`_ section for the detail.
|
|
|
|
editor/libeditor
|
|
----------------
|
|
|
|
`mozilla::EditorEventListener <https://searchfox.org/mozilla-central/source/editor/libeditor/EditorEventListener.cpp>`__
|
|
listens for trusted DOM ``compositionstart``, ``text`` and ``compositionend``
|
|
events and notifies
|
|
`mozilla::EditorBase <https://searchfox.org/mozilla-central/source/editor/libeditor/EditorBase.cpp>`__
|
|
and
|
|
`mozilla::TextEditor <https://searchfox.org/mozilla-central/source/editor/libeditor/TextEditor.cpp>`__
|
|
of the events.
|
|
|
|
When ``EditorBase`` receives an ``eCompositionStart``
|
|
(DOM ``"compositionstart"``) event, it looks for a proper ``TextComposition``
|
|
instance and stores it.
|
|
|
|
When ``TextEditor`` receives an ``eCompositionChange`` (DOM ``"text"``) event,
|
|
it creates or modifies a text node which includes the composition string and
|
|
`mozilla::CompositionTransaction <https://searchfox.org/mozilla-central/source/editor/libeditor/CompositionTransaction.cpp>`__
|
|
(it was called ``IMETextTxn``) sets IME selections for representing the clauses
|
|
of the composition string.
|
|
|
|
When ``EditorBase`` receives an ``eCompositionEnd`` (DOM ``"compositionend"``)
|
|
event, it releases the stored ``TextComposition`` instance.
|
|
|
|
nsTextFrame
|
|
-----------
|
|
``nsTextFrame`` paints IME selections.
|
|
|
|
mozilla::IMEContentObserver
|
|
---------------------------
|
|
|
|
``IMEContentObserver`` observes various changes of a focused editor. When a
|
|
corresponding element of a ``TextEditor`` or ``HTMLEditor`` instance gets
|
|
focus, an instance is created by ``IMEStateManager``, then, starts to observe
|
|
and notifies ``widget`` of IME getting focus. When the editor loses focus, it
|
|
notifies ``widget`` of IME losing focus and stops observing everything.
|
|
Finally, it's destroyed by ``IMEStateManager``.
|
|
|
|
This class observes selection changes (caret position changes), text changes of
|
|
a focused editor and layout changes (by reflow or scroll) of everything in the
|
|
document. It depends on the result of ``nsIWidget::GetIMEUpdatePreference()``
|
|
what is observed.
|
|
|
|
When this notifies ``widget`` of something, it needs to be safe to run
|
|
script because notifying something may cause dispatching one or more DOM events
|
|
and/or new reflow. Therefore, ``IMEContentObserver`` only stores which
|
|
notification should be sent to ``widget``. Then,
|
|
``mozilla::IMEContentObserver::IMENotificationSender`` tries to send the
|
|
pending notifications when it might become safe to do that. Currently, it's
|
|
tried:
|
|
|
|
* after a native event is dispatched from ``PresShell::HandleEventInternal()``
|
|
* when new focused editor receives DOM ``focus`` event
|
|
* when next refresh driver tick
|
|
|
|
.. note::
|
|
|
|
The 3rd timing may not be safe actually, but it causes a lot of oranges of
|
|
automated tests.
|
|
|
|
See also `Notifications to IME`_ section for the detail of sending
|
|
notifications.
|
|
|
|
Currently, ``WidgetQueryContentEvent`` is handled via ``IMEContentObserver``
|
|
because if it has a cache of selection, it can set reply of
|
|
``eQuerySelectedText`` event only with the cache. That is much faster than
|
|
using ``ContentEventHandler``.
|
|
|
|
e10s support
|
|
============
|
|
|
|
Even when a remote process has focus, native IME handler in chrome process does
|
|
its job. So, there is process boundary between native IME handler and focused
|
|
editor. Unfortunately, it's not allowed to use synchronous communication from
|
|
chrome process to a remote process. This means that chrome process (and also
|
|
native IME and our native IME handler) cannot query the focused editor contents
|
|
directly. For fixing this issue, we have ``ContentCache`` classes around
|
|
process boundary.
|
|
|
|
mozilla::ContentCache
|
|
---------------------
|
|
This is a base class of ``ContentCacheInChild`` and ``ContentCacheInParent``
|
|
and IPC-aware. This has common members of them including all cache data:
|
|
|
|
``mText``
|
|
Whole text in focused editor. This may be too big but IME may request all
|
|
text in the editor.
|
|
|
|
If we can separate editor contents per paragraph, moving selection between
|
|
paragraphs generates pseudo focus move, we can reduce this size and runtime
|
|
cost of ``ContentEventHandler``. However, we've not had a plan to do that
|
|
yet. Note that Microsoft Word uses this hack.
|
|
|
|
``mCompositionStart``
|
|
Offset of composition string in ``mText``. When there is no composition,
|
|
this is ``UINT32_MAX``.
|
|
|
|
``mSelection::mAnchor``, ``mSelection::mFocus``
|
|
Offset of selection anchor and focus in ``mText``.
|
|
|
|
``mSelection::mWritingMode``
|
|
Writing mode at selection start.
|
|
|
|
``mSelection::mAnchorCharRect``, ``mSelection::mFocusCharRect``
|
|
Next character rectangle of ``mSelection::mAnchor`` and
|
|
``mSelection::mFocus``. If corresponding offset is end of the editor
|
|
contents, its rectangle should be a caret rectangle.
|
|
|
|
These rectangles shouldn't be empty rect.
|
|
|
|
``mSelection::mRect``
|
|
Unified character rectangle in selection range. When the selection is
|
|
collapsed, this should be caret rect.
|
|
|
|
``mFirstRect``
|
|
First character rect of ``mText``. When ``mText`` is empty string, this
|
|
should be caret rect.
|
|
|
|
``mCaret::mOffset``
|
|
Always same as selection start offset even when selection isn't collapsed.
|
|
|
|
``mCaret::mRect``
|
|
Caret rect at ``mCaret::mOffset``. If caret isn't actually exists, it's
|
|
computed with a character rect at the offset.
|
|
|
|
``mTextRectArray::mStart``
|
|
If there is composition, ``mStart`` is same as ``mCompositionStart``.
|
|
Otherwise, ``UINT32_MAX``.
|
|
|
|
``mTextRectArray::mRects``
|
|
Each character rectangle of composition string.
|
|
|
|
``mEditorRect``
|
|
The rect of editor element.
|
|
|
|
mozilla::ContentCacheInChild
|
|
----------------------------
|
|
|
|
This exists only in remote processes. This is created as a member of
|
|
`PuppetWidget <https://searchfox.org/mozilla-central/source/widget/PuppetWidget.cpp>`__.
|
|
When ``PuppetWidget`` receives notifications to IME from ``IMEContentObserver``
|
|
in the remote process, it makes this class modify its cached content. Then,
|
|
this class do that with ``WidgetQueryContentEvents``. Finally, ``PuppetWidget``
|
|
sends the notification and ``ContentCacheInParent`` instance as
|
|
``ContentCache`` to its parent process.
|
|
|
|
mozilla::ContentCacheInParent
|
|
-----------------------------
|
|
|
|
This exists as a member of ``TabParent``. When ``TabParent`` receives
|
|
notification from corresponding remote process, it assigns
|
|
``ContentCacheInParent`` new ``ContentCache`` and post the notification to
|
|
``ContentCacheInParent``. If all sent ``WidgetCompositionEvents`` and
|
|
``WidgetSelectionEvents`` are already handled in the remote process,
|
|
``ContentCacheInParent`` sending the notifications to widget.
|
|
|
|
And also this handles ``WidgetQueryContentEvents`` with its cache. Supported
|
|
event messages of them are:
|
|
|
|
* ``eQuerySelectedText`` (only with ``SelectionType::eNormal``)
|
|
* ``eQueryTextContent``
|
|
* ``eQueryTextRect``
|
|
* ``eQueryCaretRect``
|
|
* ``eQueryEditorRect``
|
|
|
|
Additionally, this does not support query content events with XP line breakers
|
|
but this must not be any problem since native IME handlers query contents with
|
|
native line breakers.
|
|
|
|
``ContentCacheInParent`` also manages sent ``WidgetCompositionEvents`` and
|
|
``WidgetSelectionEvents``. After these events are handled in the remote
|
|
process, ``TabParent`` receives it with a call of
|
|
``RecvOnEventNeedingAckHandled()``. Then, it calls
|
|
``ContentCacheInParent::OnEventNeedingAckHandled()``. Finally,
|
|
``ContentCacheInParent`` flushes pending notifications.
|
|
|
|
How do mozilla::TextComposition and mozilla::IMEStateManager work in e10s mode?
|
|
-------------------------------------------------------------------------------
|
|
In remote process, they work as non-e10s mode. On the other hand, they work
|
|
specially in parent process.
|
|
|
|
When ``IMEStateManager`` in parent process receives ``eCompositionStart``, it
|
|
creates ``TextComposition`` instance normally. However, if the event target has
|
|
remote contents, ``TextComposition::DispatchCompositionEvent()`` directly sends
|
|
the event to the remote process instead of dispatching the event into the
|
|
target DOM tree in the process.
|
|
|
|
That means that even in a parent process, anybody can retrieve
|
|
``TextComposition`` instance, but it just does nothing in parent process.
|
|
|
|
``IMEStateManager`` works more complicated because ``IMEStateManager`` in each
|
|
process need to negotiate about owner ship of managing input context.
|
|
|
|
When a remote process gets focus, temporarily, ``IMEStateManager`` in parent
|
|
process disables IME in the widget. After that, ``IMEStateManager`` in the
|
|
remote process will set proper input context for the focused editor. At this
|
|
time, ``IMEStateManager`` in the parent process does nothing. Therefore,
|
|
``IMEContentObserver`` is never created while a remote process has focus.
|
|
|
|
When a remote process loses focus, ``IMEStateManager`` in parent process
|
|
notifies ``IMEStateManager`` in the remote process of
|
|
"Stop IME state management". When ``IMEStateManager::StopIMEStateManagement()``
|
|
is called in the remote process by this, the ``IMEStateManager`` forgets all
|
|
focus information (i.e., that indicates nobody has focus).
|
|
|
|
When ``IMEStateManager`` in parent process is notified of pseudo focus move
|
|
from or to menubar while a remote process has focus, it notifies the remote
|
|
process of "Menu keyboard listener installed". Then, ``TabChild`` calls
|
|
``IMEStateManager::OnInstalledMenuKeyboardListener()`` in the remote process.
|
|
|
|
Style of each clause
|
|
--------------------
|
|
|
|
The style of each IME selection is managed by
|
|
`LookAndFeel <https://searchfox.org/mozilla-central/source/widget/LookAndFeel.h>`__
|
|
class per platform. Therefore, it can be overridden by prefs.
|
|
|
|
Background color, foreground color (text color) and underline color can be
|
|
specified with following prefs. The values must be string of "#rrggbb" format.
|
|
|
|
* ``ui.IMERawInputBackground``
|
|
* ``ui.IMERawInputForeground``
|
|
* ``ui.IMERawInputUnderline``
|
|
* ``ui.IMESelectedRawTextBackground``
|
|
* ``ui.IMESelectedRawTextForeground``
|
|
* ``ui.IMESelectedRawTextUnderline``
|
|
* ``ui.IMEConvertedTextBackground``
|
|
* ``ui.IMEConvertedTextForeground``
|
|
* ``ui.IMEConvertedTextUnderline``
|
|
* ``ui.IMESelectedConvertedTextBackground``
|
|
* ``ui.IMESelectedConvertedTextForeground``
|
|
* ``ui.IMESelectedConvertedTextUnderline``
|
|
|
|
Underline style can be specified with the following prefs. The values are
|
|
integer, 0: none, 1: dotted, 2: dashed, 3: solid, 4: double, 5: wavy (The
|
|
values same as ``mozilla::StyleTextDecorationStyle`` defined in
|
|
`nsStyleConsts.h <https://searchfox.org/mozilla-central/source/layout/style/nsStyleConsts.h>`__).
|
|
|
|
* ``ui.IMERawInputUnderlineStyle``
|
|
* ``ui.IMESelectedRawTextUnderlineStyle``
|
|
* ``ui.IMEConvertedTextUnderlineStyle``
|
|
* ``ui.IMESelectedConvertedTextUnderlineStyle``
|
|
|
|
Underline width can be specified with ``"ui.IMEUnderlineRelativeSize"`` pref.
|
|
This affects all types of clauses. The value should be 100 or 200. 100 means
|
|
normal width, 200 means double width.
|
|
|
|
On some platforms, IME may support its own style for each clause. Currently,
|
|
this feature is supported in TSF mode of Windows and on Linux. The style
|
|
information is stored in ``TextRangeStyle`` which is defined in
|
|
`TextRange.h <https://searchfox.org/mozilla-central/source/widget/TextRange.h>`__.
|
|
It's a member of ``TextRange``. ``TextRange`` is stored in ``mRanges`` of
|
|
``WidgetCompositionEvent`` only when its message is ``eCompositionChange``.
|
|
|
|
Lifetime of composition string
|
|
==============================
|
|
|
|
When native IME notifies Gecko of starting a composition, a widget dispatches
|
|
``WidgetCompositionEvent`` with ``eCompositionStart`` which will cause a DOM
|
|
``compositionstart`` event.
|
|
|
|
When native IME notifies Gecko of a composition string change, a caret position
|
|
change and/or a change of length of clauses, a widget dispatches
|
|
``WidgetCompositionEvent`` with ``eCompositionChange`` event. It will cause a
|
|
DOM ``compositionupdate`` event when composition string is changing. That is
|
|
dispatched by ``TextComposition`` automatically. After that when the widget and
|
|
``PresShell`` of the focused editor have not been destroyed yet, the
|
|
``eCompositionChange`` will cause a DOM text event which is not in any web
|
|
standards.
|
|
|
|
When native IME notifies Gecko of the ending of a composition, a widget
|
|
dispatches ``WidgetCompositionEvent`` with ``eCompositionCommitAsIs`` or
|
|
``eCompositionCommit``. If the committing string is different from the last set
|
|
of data (i.e., if the event message is ``eCompositionCommit``),
|
|
``TextComposition`` dispatches a DOM ``compositionupdate`` event. After that,
|
|
when the widget and ``PresShell`` of the focused editor have not been destroyed
|
|
yet, an ``eCompositionChange`` event dispatched by ``TextComposition``, that
|
|
causes a DOM text event. Finally, if the widget and PresShell of the focused
|
|
editor has not been destroyed yet too, ``TextComposition`` dispatches an
|
|
``eCompositionEnd`` event which will cause a DOM compositionend event.
|
|
|
|
Limitation of handling composition
|
|
==================================
|
|
|
|
Currently, ``EditorBase`` touches undo stack at receiving every
|
|
``WidgetCompositionEvent``. Therefore, ``EditorBase`` requests to commit
|
|
composition when the following cases occur:
|
|
|
|
* The editor loses focus
|
|
* The caret is moved by mouse or Javascript
|
|
* Value of the editor is changed by Javascript
|
|
* Node of the editor is removed from DOM tree
|
|
* Somethings object is modified in an HTML editor, e.g., resizing an image
|
|
* Composition string is moved to a different position which is specified by
|
|
native IME (e.g., only a part of composition is committed)
|
|
|
|
In the future, we should fix this limitation. If we make ``EditorBase`` not
|
|
touch undo stack until composition is committed, some of the cases must be
|
|
fixed.
|
|
|
|
Notifications to IME
|
|
====================
|
|
|
|
XP part of Gecko uses ``nsIWidget::NotifyIME()`` for notifying ``widget`` of
|
|
something useful to handle IME. Note that some of them are notified only when
|
|
``nsIWidget::GetIMEUpdatePreference()`` returns flags which request the
|
|
notifications.
|
|
|
|
``NOTIFY_IME_OF_TEXT_CHANGE``, ``NOTIFY_IME_OF_SELECTION_CHANGE``,
|
|
``NOTIFY_IME_OF_POSITION_CHANGE`` and
|
|
``NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED`` are always sent by following order:
|
|
|
|
1. ``NOTIFY_IME_OF_TEXT_CHANGE``
|
|
2. ``NOTIFY_IME_OF_SELECTION_CHANGE``
|
|
3. ``NOTIFY_IME_OF_POSITION_CHANGE``
|
|
4. ``NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED``
|
|
|
|
If sending one of above notifications causes higher priority notification, the
|
|
sender should abort to send remaining notifications and restart from high
|
|
priority notification again.
|
|
|
|
Additionally, all notifications except ``NOTIFY_IME_OF_BLUR`` should be sent
|
|
only when it's safe to run script since the notification may cause querying
|
|
content and/or dispatching composition events.
|
|
|
|
NOTIFY_IME_OF_FOCUS
|
|
-------------------
|
|
|
|
When an editable editor gets focus and ``IMEContentObserver`` starts to observe
|
|
it, this is sent to widget. This must be called after the previous
|
|
``IMEContentObserver`` notified widget of ``NOTIFY_IME_OF_BLUR``.
|
|
|
|
Note that even if there are pending notifications, they are canceled when
|
|
``NOTIFY_IME_OF_FOCUS`` is sent since querying content with following
|
|
notifications immediately after getting focus does not make sense. The result
|
|
is always same as the result of querying contents at receiving this
|
|
notification.
|
|
|
|
NOTIFY_IME_OF_BLUR
|
|
------------------
|
|
|
|
When an ``IMEContentObserver`` instance ends observing the focused editor, this
|
|
is sent to ``widget`` synchronously because assumed that this notification
|
|
causes neither query content events nor composition events.
|
|
|
|
If ``widget`` wants notifications even while all windows are inactive,
|
|
``IMEContentObserver`` doesn't end observing the focused editor. I.e., in this
|
|
case, ``NOTIFY_IME_OF_FOCUS`` and ``NOTIFY_IME_OF_BLUR`` are not sent to
|
|
``widget`` when a window which has a composition is being activated or
|
|
inactivated.
|
|
|
|
When ``widget`` wants notifications during inactive, ``widget`` includes
|
|
``NOTIFY_DURING_DEACTIVE`` to the result of
|
|
``nsIWidget::GetIMEUpdatePreference()``.
|
|
|
|
If this notification is tried to sent before sending ``NOTIFY_IME_OF_FOCUS``,
|
|
all pending notifications and ``NOTIFY_IME_OF_BLUR`` itself are canceled.
|
|
|
|
NOTIFY_IME_OF_TEXT_CHANGE
|
|
-------------------------
|
|
|
|
When text of focused editor is changed, this is sent to ``widget`` with a range
|
|
of the change. But this is sent only when result of
|
|
``nsIWidget::GetIMEUpdatePreference()`` includes ``NOTIFY_TEXT_CHANGE``.
|
|
|
|
If two or more text changes occurred after previous
|
|
``NOTIFY_IME_OF_TEXT_CHANGE`` or ``NOTIFY_IME_OF_FOCUS``, the ranges of all
|
|
changes are merged. E.g., if first change is from ``1`` to ``5`` and second
|
|
change is from ``5`` to ``10``, the notified range is from ``1`` to ``10``.
|
|
|
|
If all merged text changes were caused by composition,
|
|
``IMENotification::mTextChangeData::mCausedOnlyByComposition`` is set to true.
|
|
This is useful if native IME handler wants to ignore all text changes which are
|
|
expected by native IME.
|
|
|
|
If at least one text change of the merged text changes was caused by current
|
|
composition,
|
|
``IMENotification::mTextChangeData::mIncludingChangesDuringComposition`` is set
|
|
to true. This is useful if native IME handler wants to ignore delayed text
|
|
change notifications.
|
|
|
|
If at least one text change of the merged text changes was caused when there
|
|
was no composition,
|
|
``IMENotification::mTextChangeData::mIncludingChangesWithoutComposition`` is
|
|
set to true.
|
|
|
|
NOTIFY_IME_OF_SELECTION_CHANGE
|
|
------------------------------
|
|
|
|
When selection (or caret position) is changed in focused editor, widget is
|
|
notified of this.
|
|
|
|
If the last selection change was occurred by a composition event event
|
|
handling, ``IMENotification::mSelectionChangeData::mCausedByComposition`` is
|
|
set to true. This is useful if native IME handler wants to ignore the last
|
|
selection change which is expected by native IME.
|
|
|
|
If the last selection change was occurred by an ``eSetSelection`` event,
|
|
``IMENotification::mSelectionChangeData::mCausedBySelectionEvent`` is set to
|
|
true. This is useful if native IME handler wants to ignore the last selection
|
|
change which was requested by native IME.
|
|
|
|
If the last selection is occurred during a composition,
|
|
``IMENotification::mSelectionChangeData::mOccurredDuringComposition`` is set to
|
|
true. This is useful if native IME handler wants to ignore the last selection
|
|
change which occurred by web application's ``compositionstart`` or
|
|
``compositionupdate`` event handler before inserting composition string.
|
|
|
|
NOTIFY_IME_OF_POSITION_CHANGE
|
|
-----------------------------
|
|
|
|
When reflow or scroll occurs in the document, this is sent to widget, but this
|
|
is sent only when result of ``nsIWidget::GetIMEUpdatePreference()`` includes
|
|
``NOTIFY_POSITION_CHANGE``.
|
|
|
|
This might be useful to update a candidate window position or something.
|
|
|
|
NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
|
|
---------------------------------------
|
|
|
|
After ``TextComposition`` handles ``eCompositionStart``,
|
|
``eCompositionChange``, ``eComposiitionCommit`` or ``eCompositionCommitAsIs``,
|
|
this notification is sent to widget. This might be useful to update a candidate
|
|
window position or something.
|
|
|
|
NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
|
|
--------------------------------
|
|
|
|
When a ``mousedown`` event or a ``mouseup`` event is fired on a character in a
|
|
focused editor, this is sent to widget. But this is sent only when result of
|
|
``nsIWidget::GetIMEUpdatePreference()`` includes
|
|
``NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR``. This is sent with various information.
|
|
See ``IMENotification::mMouseButtonEventData`` in
|
|
`IMEData.h <https://searchfox.org/mozilla-central/source/widget/IMEData.h>`__
|
|
for the detail.
|
|
|
|
If native IME supports mouse button event handling, ``widget`` should notify
|
|
IME of mouse button events with this. If IME consumes an event, ``widget``
|
|
should return ``NS_SUCCESS_EVENT_CONSUMED`` from ``nsIWidget::NotifyIME()``.
|
|
Then, ``EditorBase`` doesn't handle the mouse event.
|
|
|
|
Note that if a ``mousedown`` event or a ``mouseup`` event is consumed by a web
|
|
application (before a focused editor handles it), this notification is not sent
|
|
to ``widget``. This means that web applications can handle mouse button events
|
|
before IME.
|
|
|
|
Requests to IME
|
|
===============
|
|
|
|
XP part of Gecko can request IME to commit or cancel composition. This must be
|
|
requested via ``IMEStateManager::NotifyIME()``. Then, ``IMEStateManager`` looks
|
|
for a proper ``TextComposition`` instance. If it's found,
|
|
``TextComposition::RequestToCommit()`` for calling ``nsIWidget::NotifyIME()``
|
|
and handles some extra jobs.
|
|
|
|
widget should call the proper native API if it's available. Even if commit or
|
|
canceling composition does not occur synchronously, widget doesn't need to
|
|
emulate it since ``TextComposition`` will emulate it automatically. In other
|
|
words, widget should only request to commit or cancel composition to IME.
|
|
|
|
REQUEST_TO_COMMIT_COMPOSITION
|
|
-----------------------------
|
|
|
|
A request to commit current composition to IME. See also following
|
|
"`Forcibly committing composition`_" section for additional information.
|
|
|
|
REQUEST_TO_CANCEL_COMPOSITION
|
|
-----------------------------
|
|
|
|
A request to cancel current composition to IME. In other words, a request to
|
|
commit current composition with an empty string.
|
|
|
|
Forcibly committing composition
|
|
===============================
|
|
|
|
When ``TextComposition::RequestToCommit()`` calls ``nsIWidget::NotifyIME()``,
|
|
it guarantees synchronous commit or canceling composition.
|
|
|
|
In order to put it into practice, we need to handle the following four
|
|
scenarios:
|
|
|
|
The composition is committed with non-empty string synchronously
|
|
----------------------------------------------------------------
|
|
|
|
This is the most usual case. In this case, ``TextComposition`` handles
|
|
``WidgetCompositionEvent`` instances during a request normally. However, in a
|
|
remote process in e10s mode, this case never occurs since requests to native
|
|
IME is handled asynchronously.
|
|
|
|
The composition is not committed synchronously but later
|
|
--------------------------------------------------------
|
|
|
|
This is the only case in a remote process in e10s mode or occurs on Linux even
|
|
in non-e10s mode if the native IME is iBus. The callers of
|
|
``NotifyIME(REQUEST_TO_COMMIT_COMPOSITION)`` may expect that composition string
|
|
is committed immediately for their next job. For such a case,
|
|
``TextComposition::RequestToCommit()`` synthesizes DOM composition events and a
|
|
DOM text event for emulating to commit composition synchronously. Additionally,
|
|
``TextComposition`` ignores committing events which are dispatched by widget
|
|
when the widget receives native IME events.
|
|
|
|
In this case, using the last composition string as commit string.
|
|
|
|
However, if the last composition string is only an ideographic space (fullwidth
|
|
space), the composition string may be a placeholder of some old Chinese IME on
|
|
Windows.
|
|
|
|
.. image:: ChangJie.png
|
|
:alt: aScreenshot of ChangJie (Traditional Chinese IME) which puts an
|
|
ideographic space into composition string for placeholder
|
|
|
|
In this case, although, we should not commit the placeholder character because
|
|
it's not a character which the user wanted to input but we commit it as is. The
|
|
reason is, inputting an ideographic space causes a composition. Therefore, we
|
|
cannot distinguish if committing composition is unexpected. If the user uses
|
|
such old Chinese IME, ``"intl.ime.remove_placeholder_character_at_commit"``
|
|
pref may be useful but we don't support them anymore in default settings
|
|
(except if somebody will find a good way to fix this issue).
|
|
|
|
The composition is committed synchronously but with empty string
|
|
----------------------------------------------------------------
|
|
|
|
This case may occur on Linux or with some IME on other platforms. If a web
|
|
application implements autocomplete, committing with different strings
|
|
especially an empty string it might cause confusion.
|
|
|
|
In this case, TextComposition overwrites the commit string of
|
|
``eCompositionChange`` event dispatched by widget. However, if the last
|
|
composition string is only an ideographic space, it shouldn't be committed. See
|
|
the previous case.
|
|
|
|
Note that this case doesn't work as expected when composition is in a remote
|
|
process in e10s mode.
|
|
|
|
The composition is not committed
|
|
--------------------------------
|
|
|
|
On Linux, there is no API to request commit or canceling composition forcibly.
|
|
Instead, Gecko uses ``gtk_im_context_reset()`` API for this purpose because
|
|
most IME cancel composition with it. But there are some IMEs which do nothing
|
|
when Gecko calls it.
|
|
|
|
If this occurs, Gecko should restart composition with a DOM
|
|
``compositionstart`` event , a DOM ``compositionupdate`` event and a DOM
|
|
``text`` event at caret position.
|
|
|
|
.. note::
|
|
|
|
This issue hasn't been supported yet.
|
|
|
|
IME state management
|
|
====================
|
|
|
|
IME is a text input system. It means that except when a user wants to input
|
|
some text, IME shouldn't be available. For example, pressing the space key to
|
|
attempt scrolling a page may be consumed and prevented by IME. Additionally,
|
|
password editors need to request special behavior with IME.
|
|
|
|
For solving this issue, Gecko sets the proper IME state at DOM focus change.
|
|
|
|
First, when a DOM node gets focus, nsFocusManager notifies ``IMEStateManager``
|
|
of the new focused node (calls ``IMEStateManager::OnChangeFocus()``).
|
|
``IMEStateManager`` asks desired IME state by calling
|
|
``nsIContent::GetDesiredIMEState()`` of the node. If the node owns
|
|
``TextEditor`` instance, it asks for the desired IME state from the editor and
|
|
returns the result.
|
|
|
|
Next, ``IMEStateManager`` initializes ``InputContext`` (defined in
|
|
`IMEData.h <https://searchfox.org/mozilla-central/source/widget/IMEData.h>`__)
|
|
with the desired IME state and node information. Then, it calls
|
|
``nsIWidget::SetInputContext()`` with the ``InputContext``.
|
|
|
|
Finally, widget stores the InputContext and enables or disables IME if the
|
|
platform has such an API.
|
|
|
|
InputContext
|
|
------------
|
|
|
|
InputContext is a struct. Its ``mIMEState``, ``mHTMLInputType``,
|
|
``mHTMLInputInputMode`` and ``mActionHint`` are set at
|
|
``nsIWidget::SetInputContext()`` called.
|
|
|
|
mIMEState
|
|
^^^^^^^^^
|
|
IME state has two abilities. One is enabled state:
|
|
|
|
ENABLED
|
|
"""""""
|
|
|
|
This means IME is fully available. E.g., when an editable element such as
|
|
``<input type="text">``, ``<textarea>`` or ``<foo contenteditable>`` has focus.
|
|
|
|
DISABLED
|
|
""""""""
|
|
|
|
This means IME is not available. E.g., when a non-editable element has focus or
|
|
no element has focus, the desired IME state is ``DISABLED``.
|
|
|
|
PASSWORD
|
|
""""""""
|
|
|
|
This means IME state should be the same as the state when a native password
|
|
field has focus. This state is set only when
|
|
``<input type="password"> (ime-mode: auto;)``,
|
|
``<input type="text" style="ime-mode: disabled;">`` or
|
|
``<textarea style="ime-mode: disabled;">``.
|
|
|
|
The other is IME open state:
|
|
|
|
DONT_CHANGE_OPEN_STATE
|
|
""""""""""""""""""""""
|
|
|
|
The open state of IME shouldn't be changed. I.e., Gecko should keep the last
|
|
IME open state.
|
|
|
|
OPEN
|
|
""""
|
|
Open IME. This is specified only when ime-mode of the new focused element is
|
|
``active``.
|
|
|
|
CLOSE
|
|
"""""
|
|
Close IME. This is specified only when ime-mode of the new focused element is
|
|
``inactive``.
|
|
|
|
.. note::
|
|
|
|
E.g., on Linux, applications cannot manage IME open state. On such
|
|
platforms, this is ignored.
|
|
|
|
.. note::
|
|
|
|
IME open state should be changed only when ``nsIWidget::SetInputContext()``
|
|
is called at DOM focus change because changing IME open state while an
|
|
editor has focus makes users confused. The reason why
|
|
``nsIWidget::SetInputContext()`` is called is stored in
|
|
``InputContextAction::mCause``.
|
|
|
|
How does Gecko disable IME in IMM mode on Windows
|
|
"""""""""""""""""""""""""""""""""""""""""""""""""
|
|
|
|
Every window on Windows is associated an ``IMContext``. When Gecko disables
|
|
IME,
|
|
`mozilla::widget::IMEHandler <https://searchfox.org/mozilla-central/source/widget/windows/WinIMEHandler.cpp>`__::SetInputContext()
|
|
disassociates the context from the window.
|
|
|
|
How does Gecko disable IME in TSF mode on Windows
|
|
"""""""""""""""""""""""""""""""""""""""""""""""""
|
|
|
|
`mozilla::widget::TSFTextStore <https://searchfox.org/mozilla-central/source/widget/windows/TSFTextStore.cpp>`__
|
|
sets focus to a dummy context which disables the keyboard.
|
|
|
|
How does Gecko disable IME on Mac
|
|
"""""""""""""""""""""""""""""""""
|
|
|
|
`mozilla::widget::TextInputHandler <https://searchfox.org/mozilla-central/source/widget/cocoa/TextInputHandler.mm>`__::HandleKeyDownEvent()
|
|
doesn't call focused view's interpretKeyEvents. This prevents native key events
|
|
to be passed to IME.
|
|
|
|
How does Gecko disable IME on GTK
|
|
"""""""""""""""""""""""""""""""""
|
|
|
|
`mozilla::widget::IMContextWrapper <https://searchfox.org/mozilla-central/source/widget/gtk/IMContextWrapper.cpp>`__
|
|
sets focus to a dummy context which doesn't have IME composition.
|
|
|
|
How does Gecko disable IME on Android
|
|
"""""""""""""""""""""""""""""""""""""
|
|
|
|
?
|
|
|
|
mHTMLInputType
|
|
^^^^^^^^^^^^^^
|
|
|
|
The value is a string representing the focused editor.
|
|
|
|
``"text"``, ``"password"``, ``"number"``, etc.
|
|
When an ``<input>`` element gets focus, the value is the type of the input
|
|
element.
|
|
|
|
``"textarea"``
|
|
When a ``<textarea>`` element gets focus, the value is ``"textarea"``.
|
|
|
|
``""``
|
|
When an HTML editor (an element whose ``contenteditable`` attribute is
|
|
``true`` or document whose ``designMode`` is ``"on"``) gets focus, the
|
|
value is empty. And also, when the other elements get focus.
|
|
|
|
mHTMLInputMode
|
|
^^^^^^^^^^^^^^
|
|
|
|
The value is ``inputmode`` attribute value of the focused editor.
|
|
|
|
mActionHint
|
|
^^^^^^^^^^^
|
|
|
|
The value is ``enterkeyhint`` attribute value of the focused editor when
|
|
``"dom.forms.enterkeyhint"`` pref is true. This is useful for deciding the
|
|
caption for the submit button in virtual keyboard. E.g., the value could be
|
|
``"Go"``, ``"Next"`` or ``"Search"``.
|
|
|
|
Native IME handlers
|
|
===================
|
|
|
|
Following classes handles IME on each platform:
|
|
|
|
Windows
|
|
-------
|
|
|
|
`mozilla::widget::IMEHandler`__
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
This class manages input method context of each window and makes ``IMMHandler``
|
|
or ``TSFTextStore`` work with active IME and focused editor. This class has
|
|
only static members, i.e., never created its instance.
|
|
|
|
__ https://searchfox.org/mozilla-central/source/widget/windows/WinIMEHandler.cpp
|
|
|
|
`mozilla::widget::IMMHandler`__
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
This class is used when TSF mode is disabled by pref (``"intl.tsf.enabled"``
|
|
since 108, formerly named ``"intl.tsf.enable"``) or active IME is for IMM
|
|
(i.e., not TIP for TSF).
|
|
|
|
This class handles ``WM_IME_*`` messages and uses ``Imm*()`` API. This is a
|
|
singleton class since Gecko supports only on IM context in a process.
|
|
Typically, a process creates windows with default IM context. Therefore, this
|
|
design is enough (ideally, an instance should be created per IM context,
|
|
though). The singleton instance is created when it becomes necessary.
|
|
|
|
__ https://searchfox.org/mozilla-central/source/widget/windows/IMMHandler.cpp
|
|
|
|
`mozilla::widget::TSFTextStore`__
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
This class handles IME events in TSF mode and when TIP (IME implemented with
|
|
TSF) is active. This instances are created when an editable element gets focus
|
|
and released when it loses focus.
|
|
|
|
``TSFTextStore`` implements some COM interfaces which is necessary to work with
|
|
TIP. And similarly, there is a singleton class, ``TSFStaticSink``, to observe
|
|
active TIP changes.
|
|
|
|
TSF is the most complicated IME API on all platforms, therefore, design of this
|
|
class is also very complicated.
|
|
|
|
FIrst, TSF/TIP requests to lock the editor content for querying or modifying
|
|
the content or selection. However, web standards don't have such mechanism.
|
|
Therefore, when it's requested, ``TSFTextStore`` caches current content and
|
|
selection with ``WidgetQueryContentEvent``. Then, it uses the cache to reply to
|
|
query requests, and modifies the cache as they requested. At this time,
|
|
``TSFTextStore`` saves the requests of modification into the queue called
|
|
``PendingAction``. Finally, after unlocking the contents, it flushes the
|
|
pending actions with dispatches ``WidgetCompositionEvent``s via
|
|
``TextEventDispatcher``.
|
|
|
|
Then, ``IMEContentObserver`` will notify some changes caused by the dispatched
|
|
``WidgetCompositionEvents`` (they are notified synchronously in chrome or
|
|
non-e10s mode, but asynchronously from a remote process in e10s mode). At this
|
|
time, ``TSFTextStore`` may receive notifications which indicates web
|
|
application changes the content differently from cache in ``TSFTextStore``.
|
|
However, ``TSFTextStore`` ignores such fact temporarily until the composition
|
|
is finished completely. The reason is that, notifying unexpected text or
|
|
selection changes to TSF and/or TIP during composition may behave them odd.
|
|
|
|
When a composition is committed and it receives
|
|
``NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED``, ``TSFTextStore`` clears the cache
|
|
of contents and notifying TSF of merged text changes and the last selection
|
|
change if they are not caused by composition. By this step, TSF and TIP may
|
|
sync its internal cache with actual contents.
|
|
|
|
Note that if new composition is started before
|
|
``NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED`` notification, ``TSFTextStore``
|
|
handles the a composition with cached contents which may be different from
|
|
actual contents. So, e.g., reconversion around caret may not work as unexpected
|
|
in such case, but we don't have a good solution for this issue.
|
|
|
|
On the other hand, ``TSFTextStore`` cannot cache character rectangles since if
|
|
there are a lot of characters, caching the rectangles require a lot of CPU cost
|
|
(to compute each rect) and memory. Therefore, ``TSFTextStore`` will use
|
|
insertion point relative query for them
|
|
`bug 1286157 <https://bugzilla.mozilla.org/show_bug.cgi?id=1286157>`__. Then,
|
|
it can retrieve expected character's rect even if the cache of ``TSFTextStore``
|
|
is different from the actual contents because TIP typically needs caret
|
|
position's character rect (for a popup to indicate current input mode or next
|
|
word suggestion list) or first character rect of the target clause of current
|
|
composition (for a candidate list window of conversion).
|
|
|
|
__ https://searchfox.org/mozilla-central/source/widget/windows/TSFTextStore.cpp
|
|
|
|
Mac
|
|
---
|
|
|
|
Both IME and key events are handled in
|
|
`TextInputHandler.mm <https://searchfox.org/mozilla-central/source/widget/cocoa/TextInputHandler.mm>`__.
|
|
|
|
``mozilla::widget::TextInputHandlerBase`` is the most base class.
|
|
``mozilla::widget::IMEInputHandler`` inherits ``TextInputHandlerBase`` and
|
|
handles IME related events. ``mozilla::widget::TextInputHandler`` inherits
|
|
``TextInputHandlerBase`` and implements ``NSTextInput`` protocol of Cocoa. Its
|
|
instance is created per
|
|
`nsChildView <https://searchfox.org/mozilla-central/source/widget/cocoa/nsChildView.mm>`__
|
|
instance.
|
|
|
|
GTK
|
|
---
|
|
|
|
`mozilla::widget::IMContextWrapper <https://searchfox.org/mozilla-central/source/widget/gtk/IMContextWrapper.cpp>`__
|
|
handles IME. The instance is created per top level window.
|
|
|
|
Android
|
|
-------
|
|
|
|
`org.mozilla.geckoview.GeckoEditable <https://searchfox.org/mozilla-central/source/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java>`__ handles native IME events and `mozilla::widget::GeckoEditableSupport <https://searchfox.org/mozilla-central/source/widget/android/GeckoEditableSupport.cpp>`__
|
|
dispatches ``Widget*Event``.
|