Bug 1643246 - Don't use attribute selectors for determining if a select is a drop...
[gecko.git] / widget / TextEventDispatcher.h
blob557811fd1ea0140c42670ee0f589212667bb1d5e
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_
9 #include "mozilla/RefPtr.h"
10 #include "nsString.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/TextEventDispatcherListener.h"
14 #include "mozilla/TextRange.h"
15 #include "mozilla/widget/IMEData.h"
17 class nsIWidget;
19 namespace mozilla {
20 namespace widget {
22 class PuppetWidget;
24 /**
25 * TextEventDispatcher is a helper class for dispatching widget events defined
26 * in TextEvents.h. Currently, this is a helper for dispatching
27 * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior
28 * of them for conforming to DOM Level 3 Events.
29 * An instance of this class is created by nsIWidget instance and owned by it.
30 * This is typically created only by the top level widgets because only they
31 * handle IME.
34 class TextEventDispatcher final {
35 ~TextEventDispatcher() = default;
37 NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
39 public:
40 explicit TextEventDispatcher(nsIWidget* aWidget);
42 /**
43 * Initializes the instance for IME or automated test. Either IME or tests
44 * need to call one of them before starting composition. If they return
45 * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
46 * notifications from TextEventDispatcher for same purpose (for IME or tests).
47 * If this returns another error, the caller shouldn't keep starting
48 * composition.
50 * @param aListener Specify the listener to listen notifications and
51 * requests. This must not be null.
52 * NOTE: aListener is stored as weak reference in
53 * TextEventDispatcher. See mListener
54 * definition below.
56 nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
57 nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener,
58 bool aIsAPZAware);
59 nsresult BeginNativeInputTransaction();
61 /**
62 * BeginInputTransactionFor() should be used when aPuppetWidget dispatches
63 * a composition or keyboard event coming from its parent process.
65 nsresult BeginInputTransactionFor(const WidgetGUIEvent* aEvent,
66 PuppetWidget* aPuppetWidget);
68 /**
69 * EndInputTransaction() should be called when the listener stops using
70 * the TextEventDispatcher.
72 * @param aListener The listener using the TextEventDispatcher instance.
74 void EndInputTransaction(TextEventDispatcherListener* aListener);
76 /**
77 * OnDestroyWidget() is called when mWidget is being destroyed.
79 void OnDestroyWidget();
81 nsIWidget* GetWidget() const { return mWidget; }
83 const IMENotificationRequests& IMENotificationRequestsRef() const {
84 return mIMENotificationRequests;
87 /**
88 * OnWidgetChangeIMENotificationRequests() is called when aWidget's
89 * IMENotificationRequest is maybe modified by unusual path. E.g.,
90 * modified in an async path.
92 void OnWidgetChangeIMENotificationRequests(nsIWidget* aWidget) {
93 MOZ_ASSERT(aWidget);
94 if (mWidget == aWidget) {
95 UpdateNotificationRequests();
99 /**
100 * GetState() returns current state of this class.
102 * @return NS_OK: Fine to compose text.
103 * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
104 * BeginInputTransactionForTests()
105 * should be called.
106 * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
107 * composition.
109 nsresult GetState() const;
112 * IsComposing() returns true after calling StartComposition() and before
113 * calling CommitComposition(). In other words, native IME has composition
114 * when this returns true.
116 bool IsComposing() const { return mIsComposing; }
119 * IsHandlingComposition() returns true after calling StartComposition() and
120 * content has not handled eCompositionCommit(AsIs) event. In other words,
121 * our content has composition when this returns true.
123 bool IsHandlingComposition() const { return mIsHandlingComposition; }
126 * IsInNativeInputTransaction() returns true if native IME handler began a
127 * transaction and it's not finished yet.
129 bool IsInNativeInputTransaction() const {
130 return mInputTransactionType == eNativeInputTransaction;
134 * IsDispatchingEvent() returns true while this instance dispatching an event.
136 bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
139 * GetPseudoIMEContext() returns pseudo native IME context if there is an
140 * input transaction whose type is not for native event handler.
141 * Otherwise, returns nullptr.
143 void* GetPseudoIMEContext() const {
144 if (mInputTransactionType == eNoInputTransaction ||
145 mInputTransactionType == eNativeInputTransaction) {
146 return nullptr;
148 return const_cast<TextEventDispatcher*>(this);
152 * StartComposition() starts composition explicitly.
154 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
155 * be initialized with this. Otherwise, initialized
156 * with the time at initializing.
158 nsresult StartComposition(nsEventStatus& aStatus,
159 const WidgetEventTime* aEventTime = nullptr);
162 * CommitComposition() commits composition.
164 * @param aCommitString If this is null, commits with the last composition
165 * string. Otherwise, commits the composition with
166 * this value.
167 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
168 * be initialized with this. Otherwise, initialized
169 * with the time at initializing.
171 nsresult CommitComposition(nsEventStatus& aStatus,
172 const nsAString* aCommitString = nullptr,
173 const WidgetEventTime* aEventTime = nullptr);
176 * SetPendingCompositionString() sets new composition string which will be
177 * dispatched with eCompositionChange event by calling Flush().
179 * @param aString New composition string.
181 nsresult SetPendingCompositionString(const nsAString& aString) {
182 return mPendingComposition.SetString(aString);
186 * AppendClauseToPendingComposition() appends a clause information to
187 * the pending composition string.
189 * @param aLength Length of the clause.
190 * @param aTextRangeType One of TextRangeType::eRawClause,
191 * TextRangeType::eSelectedRawClause,
192 * TextRangeType::eConvertedClause or
193 * TextRangeType::eSelectedClause.
195 nsresult AppendClauseToPendingComposition(uint32_t aLength,
196 TextRangeType aTextRangeType) {
197 return mPendingComposition.AppendClause(aLength, aTextRangeType);
201 * SetCaretInPendingComposition() sets caret position in the pending
202 * composition string and its length. This is optional. If IME doesn't
203 * want to show caret, it shouldn't need to call this.
205 * @param aOffset Offset of the caret in the pending composition
206 * string. This should not be larger than the length
207 * of the pending composition string.
208 * @param aLength Caret width. If this is 0, caret will be collapsed.
209 * Note that Gecko doesn't supported wide caret yet,
210 * therefore, this is ignored for now.
212 nsresult SetCaretInPendingComposition(uint32_t aOffset, uint32_t aLength) {
213 return mPendingComposition.SetCaret(aOffset, aLength);
217 * SetPendingComposition() is useful if native IME handler already creates
218 * array of clauses and/or caret information.
220 * @param aString Composition string. This may include native line
221 * breakers since they will be replaced with XP line
222 * breakers automatically.
223 * @param aRanges This should include the ranges of clauses and/or
224 * a range of caret. Note that this method allows
225 * some ranges overlap each other and the range order
226 * is not from start to end.
228 nsresult SetPendingComposition(const nsAString& aString,
229 const TextRangeArray* aRanges) {
230 return mPendingComposition.Set(aString, aRanges);
234 * FlushPendingComposition() sends the pending composition string
235 * to the widget of the store DOM window. Before calling this, IME needs to
236 * set pending composition string with SetPendingCompositionString(),
237 * AppendClauseToPendingComposition() and/or
238 * SetCaretInPendingComposition().
240 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
241 * be initialized with this. Otherwise, initialized
242 * with the time at initializing.
244 nsresult FlushPendingComposition(
245 nsEventStatus& aStatus, const WidgetEventTime* aEventTime = nullptr) {
246 return mPendingComposition.Flush(this, aStatus, aEventTime);
250 * ClearPendingComposition() makes this instance forget pending composition.
252 void ClearPendingComposition() { mPendingComposition.Clear(); }
255 * GetPendingCompositionClauses() returns text ranges which was appended by
256 * AppendClauseToPendingComposition() or SetPendingComposition().
258 const TextRangeArray* GetPendingCompositionClauses() const {
259 return mPendingComposition.GetClauses();
263 * @see nsIWidget::NotifyIME()
265 nsresult NotifyIME(const IMENotification& aIMENotification);
268 * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
270 * @param aMessage Must be eKeyDown or eKeyUp.
271 * Use MaybeDispatchKeypressEvents() for dispatching
272 * eKeyPress.
273 * @param aKeyboardEvent A keyboard event.
274 * @param aStatus If dispatching event should be marked as consumed,
275 * set nsEventStatus_eConsumeNoDefault. Otherwise,
276 * set nsEventStatus_eIgnore. After dispatching
277 * a event and it's consumed this returns
278 * nsEventStatus_eConsumeNoDefault.
279 * @param aData Calling this method may cause calling
280 * WillDispatchKeyboardEvent() of the listener.
281 * aData will be set to its argument.
282 * @return true if an event is dispatched. Otherwise, false.
284 bool DispatchKeyboardEvent(EventMessage aMessage,
285 const WidgetKeyboardEvent& aKeyboardEvent,
286 nsEventStatus& aStatus, void* aData = nullptr);
289 * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
290 * generated from aKeydownEvent.
292 * @param aKeyboardEvent A keyboard event.
293 * @param aStatus Sets the result when the caller dispatches
294 * aKeyboardEvent. Note that if the value is
295 * nsEventStatus_eConsumeNoDefault, this does NOT
296 * dispatch keypress events.
297 * When this method dispatches one or more keypress
298 * events and one of them is consumed, this returns
299 * nsEventStatus_eConsumeNoDefault.
300 * @param aData Calling this method may cause calling
301 * WillDispatchKeyboardEvent() of the listener.
302 * aData will be set to its argument.
303 * @param aNeedsCallback Set true when caller needs to initialize each
304 * eKeyPress event immediately before dispatch.
305 * Then, WillDispatchKeyboardEvent() is always called.
306 * @return true if one or more events are dispatched.
307 * Otherwise, false.
309 bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
310 nsEventStatus& aStatus,
311 void* aData = nullptr,
312 bool aNeedsCallback = false);
314 private:
315 // mWidget is owner of the instance. When this is created, this is set.
316 // And when mWidget is released, this is cleared by OnDestroyWidget().
317 // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
318 // return true).
319 nsIWidget* mWidget;
320 // mListener is a weak reference to TextEventDispatcherListener. That might
321 // be referred by JS. Therefore, the listener might be difficult to release
322 // itself if this is a strong reference. Additionally, it's difficult to
323 // check if a method to uninstall the listener is called by valid instance.
324 // So, using weak reference is the best way in this case.
325 nsWeakPtr mListener;
326 // mIMENotificationRequests should store current IME's notification requests.
327 // So, this may be invalid when IME doesn't have focus.
328 IMENotificationRequests mIMENotificationRequests;
330 // mPendingComposition stores new composition string temporarily.
331 // These values will be used for dispatching eCompositionChange event
332 // in Flush(). When Flush() is called, the members will be cleared
333 // automatically.
334 class PendingComposition {
335 public:
336 PendingComposition();
337 nsresult SetString(const nsAString& aString);
338 nsresult AppendClause(uint32_t aLength, TextRangeType aTextRangeType);
339 nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
340 nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
341 nsresult Flush(TextEventDispatcher* aDispatcher, nsEventStatus& aStatus,
342 const WidgetEventTime* aEventTime);
343 const TextRangeArray* GetClauses() const { return mClauses; }
344 void Clear();
346 private:
347 nsString mString;
348 RefPtr<TextRangeArray> mClauses;
349 TextRange mCaret;
350 bool mReplacedNativeLineBreakers;
352 void EnsureClauseArray();
355 * ReplaceNativeLineBreakers() replaces "\r\n" and "\r" to "\n" and adjust
356 * each clause information and the caret information.
358 void ReplaceNativeLineBreakers();
361 * AdjustRange() adjusts aRange as in the string with XP line breakers.
363 * @param aRange The reference to a range in aNativeString.
364 * This will be modified.
365 * @param aNativeString The string with native line breakers.
366 * This may include "\r\n" and/or "\r".
368 static void AdjustRange(TextRange& aRange, const nsAString& aNativeString);
370 PendingComposition mPendingComposition;
372 // While dispatching an event, this is incremented.
373 uint16_t mDispatchingEvent;
375 enum InputTransactionType : uint8_t {
376 // No input transaction has been started.
377 eNoInputTransaction,
378 // Input transaction for native IME or keyboard event handler. Note that
379 // keyboard events may be dispatched via parent process if there is.
380 // In remote processes, this is also used when events come from the parent
381 // process and are not for tests because we cannot distinguish if
382 // TextEventDispatcher has which type of transaction when it dispatches
383 // (eNativeInputTransaction or eSameProcessSyncInputTransaction).
384 eNativeInputTransaction,
385 // Input transaction for automated tests which are APZ-aware. Note that
386 // keyboard events may be dispatched via parent process if there is.
387 eAsyncTestInputTransaction,
388 // Input transaction for automated tests which assume events are fired
389 // synchronously. I.e., keyboard events are always dispatched in the
390 // current process.
391 // In remote processes, this is also used when events come from the parent
392 // process and are not dispatched by the instance itself for APZ-aware
393 // tests because this instance won't dispatch the events via the parent
394 // process again.
395 eSameProcessSyncTestInputTransaction,
396 // Input transaction for others (currently, only FuzzingFunctions).
397 // Events are fired synchronously in the process.
398 // XXX Should we make this async for testing default action handlers in
399 // the main process?
400 eSameProcessSyncInputTransaction
403 InputTransactionType mInputTransactionType;
405 bool IsForTests() const {
406 return mInputTransactionType == eAsyncTestInputTransaction ||
407 mInputTransactionType == eSameProcessSyncTestInputTransaction;
410 // ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should
411 // be dispatched via its parent process (if there is) for APZ. Otherwise,
412 // when the input transaction is for IME of B2G or automated tests which
413 // isn't APZ-aware, WidgetInputEvent should be dispatched form current
414 // process directly.
415 bool ShouldSendInputEventToAPZ() const {
416 switch (mInputTransactionType) {
417 case eNativeInputTransaction:
418 case eAsyncTestInputTransaction:
419 return true;
420 case eSameProcessSyncTestInputTransaction:
421 case eSameProcessSyncInputTransaction:
422 return false;
423 case eNoInputTransaction:
424 NS_WARNING(
425 "Why does the caller need to dispatch an event when "
426 "there is no input transaction?");
427 return true;
428 default:
429 MOZ_CRASH("Define the behavior of new InputTransactionType");
433 // See IsComposing().
434 bool mIsComposing;
436 // See IsHandlingComposition().
437 bool mIsHandlingComposition;
439 // true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not
440 // received yet. Otherwise, false.
441 bool mHasFocus;
443 nsresult BeginInputTransactionInternal(TextEventDispatcherListener* aListener,
444 InputTransactionType aType);
447 * InitEvent() initializes aEvent. This must be called before dispatching
448 * the event.
450 void InitEvent(WidgetGUIEvent& aEvent) const;
453 * DispatchEvent() dispatches aEvent on aWidget.
455 nsresult DispatchEvent(nsIWidget* aWidget, WidgetGUIEvent& aEvent,
456 nsEventStatus& aStatus);
459 * DispatchInputEvent() dispatches aEvent on aWidget.
461 nsresult DispatchInputEvent(nsIWidget* aWidget, WidgetInputEvent& aEvent,
462 nsEventStatus& aStatus);
465 * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
466 * been started it yet.
468 * @param aStatus If it succeeded to start composition normally, this
469 * returns nsEventStatus_eIgnore. Otherwise, e.g.,
470 * the composition is canceled during dispatching
471 * compositionstart event, this returns
472 * nsEventStatus_eConsumeNoDefault. In this case,
473 * the caller shouldn't keep doing its job.
474 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
475 * be initialized with this. Otherwise, initialized
476 * with the time at initializing.
477 * @return Only when something unexpected occurs, this returns
478 * an error. Otherwise, returns NS_OK even if aStatus
479 * is nsEventStatus_eConsumeNoDefault.
481 nsresult StartCompositionAutomaticallyIfNecessary(
482 nsEventStatus& aStatus, const WidgetEventTime* aEventTime);
485 * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
487 * @param aMessage Must be eKeyDown, eKeyUp or eKeyPress.
488 * @param aKeyboardEvent A keyboard event. If aMessage is eKeyPress and
489 * the event is for second or later character, its
490 * mKeyValue should be empty string.
491 * @param aStatus If dispatching event should be marked as consumed,
492 * set nsEventStatus_eConsumeNoDefault. Otherwise,
493 * set nsEventStatus_eIgnore. After dispatching
494 * a event and it's consumed this returns
495 * nsEventStatus_eConsumeNoDefault.
496 * @param aData Calling this method may cause calling
497 * WillDispatchKeyboardEvent() of the listener.
498 * aData will be set to its argument.
499 * @param aIndexOfKeypress This must be 0 if aMessage isn't eKeyPress or
500 * aKeyboard.mKeyNameIndex isn't
501 * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
502 * when an eKeyPress event causes inputting
503 * text, this must be between 0 and
504 * mKeyValue.Length() - 1 since keypress events
505 * sending only one character per event.
506 * @param aNeedsCallback Set true when caller needs to initialize each
507 * eKeyPress event immediately before dispatch.
508 * Then, WillDispatchKeyboardEvent() is always called.
509 * @return true if an event is dispatched. Otherwise, false.
511 bool DispatchKeyboardEventInternal(EventMessage aMessage,
512 const WidgetKeyboardEvent& aKeyboardEvent,
513 nsEventStatus& aStatus, void* aData,
514 uint32_t aIndexOfKeypress = 0,
515 bool aNeedsCallback = false);
518 * ClearNotificationRequests() clears mIMENotificationRequests.
520 void ClearNotificationRequests();
523 * UpdateNotificationRequests() updates mIMENotificationRequests with
524 * current state. If the instance doesn't have focus, this clears
525 * mIMENotificationRequests. Otherwise, updates it with both requests of
526 * current listener and native listener.
528 void UpdateNotificationRequests();
531 } // namespace widget
532 } // namespace mozilla
534 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_