1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_textcompositionsynthesizer_h_
7 #define mozilla_textcompositionsynthesizer_h_
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/TextEventDispatcherListener.h"
14 #include "mozilla/TextRange.h"
21 struct IMENotification
;
24 * TextEventDispatcher is a helper class for dispatching widget events defined
25 * in TextEvents.h. Currently, this is a helper for dispatching
26 * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior
27 * of them for conforming to DOM Level 3 Events.
28 * An instance of this class is created by nsIWidget instance and owned by it.
29 * This is typically created only by the top level widgets because only they
33 class TextEventDispatcher final
35 ~TextEventDispatcher()
39 NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher
)
42 explicit TextEventDispatcher(nsIWidget
* aWidget
);
45 * Initializes the instance for IME or automated test. Either IME or tests
46 * need to call one of them before starting composition. If they return
47 * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
48 * notifications from TextEventDispatcher for same purpose (for IME or tests).
49 * If this returns another error, the caller shouldn't keep starting
52 * @param aListener Specify the listener to listen notifications and
53 * requests. This must not be null.
54 * NOTE: aListener is stored as weak reference in
55 * TextEventDispatcher. See mListener
58 nsresult
BeginInputTransaction(TextEventDispatcherListener
* aListener
);
59 nsresult
BeginInputTransactionForTests(
60 TextEventDispatcherListener
* aListener
);
63 * OnDestroyWidget() is called when mWidget is being destroyed.
65 void OnDestroyWidget();
68 * GetState() returns current state of this class.
70 * @return NS_OK: Fine to compose text.
71 * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
72 * BeginInputTransactionForTests()
74 * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
77 nsresult
GetState() const;
80 * IsComposing() returns true after calling StartComposition() and before
81 * calling CommitComposition().
83 bool IsComposing() const { return mIsComposing
; }
86 * IsDispatchingEvent() returns true while this instance dispatching an event.
88 bool IsDispatchingEvent() const { return mDispatchingEvent
> 0; }
91 * StartComposition() starts composition explicitly.
93 nsresult
StartComposition(nsEventStatus
& aStatus
);
96 * CommitComposition() commits composition.
98 * @param aCommitString If this is null, commits with the last composition
99 * string. Otherwise, commits the composition with
102 nsresult
CommitComposition(nsEventStatus
& aStatus
,
103 const nsAString
* aCommitString
= nullptr);
106 * SetPendingCompositionString() sets new composition string which will be
107 * dispatched with NS_COMPOSITION_CHANGE event by calling Flush().
109 * @param aString New composition string.
111 nsresult
SetPendingCompositionString(const nsAString
& aString
)
113 return mPendingComposition
.SetString(aString
);
117 * AppendClauseToPendingComposition() appends a clause information to
118 * the pending composition string.
120 * @param aLength Length of the clause.
121 * @param aAttribute One of NS_TEXTRANGE_RAWINPUT,
122 * NS_TEXTRANGE_SELECTEDRAWTEXT,
123 * NS_TEXTRANGE_CONVERTEDTEXT or
124 * NS_TEXTRANGE_SELECTEDCONVERTEDTEXT.
126 nsresult
AppendClauseToPendingComposition(uint32_t aLength
,
129 return mPendingComposition
.AppendClause(aLength
, aAttribute
);
133 * SetCaretInPendingComposition() sets caret position in the pending
134 * composition string and its length. This is optional. If IME doesn't
135 * want to show caret, it shouldn't need to call this.
137 * @param aOffset Offset of the caret in the pending composition
138 * string. This should not be larger than the length
139 * of the pending composition string.
140 * @param aLength Caret width. If this is 0, caret will be collapsed.
141 * Note that Goanna doesn't supported wide caret yet,
142 * therefore, this is ignored for now.
144 nsresult
SetCaretInPendingComposition(uint32_t aOffset
,
147 return mPendingComposition
.SetCaret(aOffset
, aLength
);
151 * FlushPendingComposition() sends the pending composition string
152 * to the widget of the store DOM window. Before calling this, IME needs to
153 * set pending composition string with SetPendingCompositionString(),
154 * AppendClauseToPendingComposition() and/or
155 * SetCaretInPendingComposition().
157 nsresult
FlushPendingComposition(nsEventStatus
& aStatus
)
159 return mPendingComposition
.Flush(this, aStatus
);
163 * ClearPendingComposition() makes this instance forget pending composition.
165 void ClearPendingComposition()
167 mPendingComposition
.Clear();
171 * @see nsIWidget::NotifyIME()
173 nsresult
NotifyIME(const IMENotification
& aIMENotification
);
176 * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
178 * @param aMessage Must be NS_KEY_DOWN or NS_KEY_UP.
179 * Use MaybeDispatchKeypressEvents() for dispatching
181 * @param aKeyboardEvent A keyboard event.
182 * @param aStatus If dispatching event should be marked as consumed,
183 * set nsEventStatus_eConsumeNoDefault. Otherwise,
184 * set nsEventStatus_eIgnore. After dispatching
185 * a event and it's consumed this returns
186 * nsEventStatus_eConsumeNoDefault.
187 * @return true if an event is dispatched. Otherwise, false.
189 bool DispatchKeyboardEvent(uint32_t aMessage
,
190 const WidgetKeyboardEvent
& aKeyboardEvent
,
191 nsEventStatus
& aStatus
);
194 * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
195 * generated from aKeydownEvent.
197 * @param aKeyboardEvent A keyboard event.
198 * @param aStatus Sets the result when the caller dispatches
199 * aKeyboardEvent. Note that if the value is
200 * nsEventStatus_eConsumeNoDefault, this does NOT
201 * dispatch keypress events.
202 * When this method dispatches one or more keypress
203 * events and one of them is consumed, this returns
204 * nsEventStatus_eConsumeNoDefault.
205 * @return true if one or more events are dispatched.
208 bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent
& aKeyboardEvent
,
209 nsEventStatus
& aStatus
);
212 // mWidget is owner of the instance. When this is created, this is set.
213 // And when mWidget is released, this is cleared by OnDestroyWidget().
214 // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
217 // mListener is a weak reference to TextEventDispatcherListener. That might
218 // be referred by JS. Therefore, the listener might be difficult to release
219 // itself if this is a strong reference. Additionally, it's difficult to
220 // check if a method to uninstall the listener is called by valid instance.
221 // So, using weak reference is the best way in this case.
224 // mPendingComposition stores new composition string temporarily.
225 // These values will be used for dispatching NS_COMPOSITION_CHANGE event
226 // in Flush(). When Flush() is called, the members will be cleared
228 class PendingComposition
231 PendingComposition();
232 nsresult
SetString(const nsAString
& aString
);
233 nsresult
AppendClause(uint32_t aLength
, uint32_t aAttribute
);
234 nsresult
SetCaret(uint32_t aOffset
, uint32_t aLength
);
235 nsresult
Flush(TextEventDispatcher
* aDispatcher
, nsEventStatus
& aStatus
);
239 nsAutoString mString
;
240 nsRefPtr
<TextRangeArray
> mClauses
;
243 void EnsureClauseArray();
245 PendingComposition mPendingComposition
;
247 // While dispatching an event, this is incremented.
248 uint16_t mDispatchingEvent
;
251 // See IsComposing().
254 // If this is true, keydown and keyup events are dispatched even when there
256 static bool sDispatchKeyEventsDuringComposition
;
258 nsresult
BeginInputTransactionInternal(
259 TextEventDispatcherListener
* aListener
,
263 * InitEvent() initializes aEvent. This must be called before dispatching
266 void InitEvent(WidgetGUIEvent
& aEvent
) const;
269 * DispatchEvent() dispatches aEvent on aWidget.
271 nsresult
DispatchEvent(nsIWidget
* aWidget
,
272 WidgetGUIEvent
& aEvent
,
273 nsEventStatus
& aStatus
);
276 * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
277 * been started it yet.
279 * @param aStatus If it succeeded to start composition normally, this
280 * returns nsEventStatus_eIgnore. Otherwise, e.g.,
281 * the composition is canceled during dispatching
282 * compositionstart event, this returns
283 * nsEventStatus_eConsumeNoDefault. In this case,
284 * the caller shouldn't keep doing its job.
285 * @return Only when something unexpected occurs, this returns
286 * an error. Otherwise, returns NS_OK even if aStatus
287 * is nsEventStatus_eConsumeNoDefault.
289 nsresult
StartCompositionAutomaticallyIfNecessary(nsEventStatus
& aStatus
);
292 * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
294 * @param aMessage Must be NS_KEY_DOWN, NS_KEY_UP or NS_KEY_PRESS.
295 * @param aKeyboardEvent A keyboard event. If aMessage is NS_KEY_PRESS and
296 * the event is for second or later character, its
297 * mKeyValue should be empty string.
298 * @param aStatus If dispatching event should be marked as consumed,
299 * set nsEventStatus_eConsumeNoDefault. Otherwise,
300 * set nsEventStatus_eIgnore. After dispatching
301 * a event and it's consumed this returns
302 * nsEventStatus_eConsumeNoDefault.
303 * @param aIndexOfKeypress This must be 0 if aMessage isn't NS_KEY_PRESS or
304 * aKeyboard.mKeyNameIndex isn't
305 * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
306 * when an NS_KEY_PRESS event causes inputting
307 * text, this must be between 0 and
308 * mKeyValue.Length() - 1 since keypress events
309 * sending only one character per event.
310 * @return true if an event is dispatched. Otherwise, false.
312 bool DispatchKeyboardEventInternal(uint32_t aMessage
,
313 const WidgetKeyboardEvent
& aKeyboardEvent
,
314 nsEventStatus
& aStatus
,
315 uint32_t aIndexOfKeypress
= 0);
318 } // namespace widget
319 } // namespace mozilla
321 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_