Bug 1477919 [wpt PR 12154] - url: DecodeURLEscapeSequences() should not apply UTF...
[gecko.git] / widget / TextEventDispatcher.h
blobcc20acd1ec747329192bb0a4c2eaeb5f37a1a74b
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
36 ~TextEventDispatcher()
40 NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
42 public:
43 explicit TextEventDispatcher(nsIWidget* aWidget);
45 /**
46 * Initializes the instance for IME or automated test. Either IME or tests
47 * need to call one of them before starting composition. If they return
48 * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
49 * notifications from TextEventDispatcher for same purpose (for IME or tests).
50 * If this returns another error, the caller shouldn't keep starting
51 * composition.
53 * @param aListener Specify the listener to listen notifications and
54 * requests. This must not be null.
55 * NOTE: aListener is stored as weak reference in
56 * TextEventDispatcher. See mListener
57 * definition below.
59 nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
60 nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener,
61 bool aIsAPZAware);
62 nsresult BeginNativeInputTransaction();
64 /**
65 * BeginInputTransactionFor() should be used when aPuppetWidget dispatches
66 * a composition or keyboard event coming from its parent process.
68 nsresult BeginInputTransactionFor(const WidgetGUIEvent* aEvent,
69 PuppetWidget* aPuppetWidget);
71 /**
72 * EndInputTransaction() should be called when the listener stops using
73 * the TextEventDispatcher.
75 * @param aListener The listener using the TextEventDispatcher instance.
77 void EndInputTransaction(TextEventDispatcherListener* aListener);
79 /**
80 * OnDestroyWidget() is called when mWidget is being destroyed.
82 void OnDestroyWidget();
84 nsIWidget* GetWidget() const { return mWidget; }
86 const IMENotificationRequests& IMENotificationRequestsRef() const
88 return mIMENotificationRequests;
91 /**
92 * GetState() returns current state of this class.
94 * @return NS_OK: Fine to compose text.
95 * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
96 * BeginInputTransactionForTests()
97 * should be called.
98 * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
99 * composition.
101 nsresult GetState() const;
104 * IsComposing() returns true after calling StartComposition() and before
105 * calling CommitComposition(). In other words, native IME has composition
106 * when this returns true.
108 bool IsComposing() const { return mIsComposing; }
111 * IsHandlingComposition() returns true after calling StartComposition() and
112 * content has not handled eCompositionCommit(AsIs) event. In other words,
113 * our content has composition when this returns true.
115 bool IsHandlingComposition() const { return mIsHandlingComposition; }
118 * IsInNativeInputTransaction() returns true if native IME handler began a
119 * transaction and it's not finished yet.
121 bool IsInNativeInputTransaction() const
123 return mInputTransactionType == eNativeInputTransaction;
127 * IsDispatchingEvent() returns true while this instance dispatching an event.
129 bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
132 * GetPseudoIMEContext() returns pseudo native IME context if there is an
133 * input transaction whose type is not for native event handler.
134 * Otherwise, returns nullptr.
136 void* GetPseudoIMEContext() const
138 if (mInputTransactionType == eNoInputTransaction ||
139 mInputTransactionType == eNativeInputTransaction) {
140 return nullptr;
142 return const_cast<TextEventDispatcher*>(this);
146 * StartComposition() starts composition explicitly.
148 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
149 * be initialized with this. Otherwise, initialized
150 * with the time at initializing.
152 nsresult StartComposition(nsEventStatus& aStatus,
153 const WidgetEventTime* aEventTime = nullptr);
156 * CommitComposition() commits composition.
158 * @param aCommitString If this is null, commits with the last composition
159 * string. Otherwise, commits the composition with
160 * this value.
161 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
162 * be initialized with this. Otherwise, initialized
163 * with the time at initializing.
165 nsresult CommitComposition(nsEventStatus& aStatus,
166 const nsAString* aCommitString = nullptr,
167 const WidgetEventTime* aEventTime = nullptr);
170 * SetPendingCompositionString() sets new composition string which will be
171 * dispatched with eCompositionChange event by calling Flush().
173 * @param aString New composition string.
175 nsresult SetPendingCompositionString(const nsAString& aString)
177 return mPendingComposition.SetString(aString);
181 * AppendClauseToPendingComposition() appends a clause information to
182 * the pending composition string.
184 * @param aLength Length of the clause.
185 * @param aTextRangeType One of TextRangeType::eRawClause,
186 * TextRangeType::eSelectedRawClause,
187 * TextRangeType::eConvertedClause or
188 * TextRangeType::eSelectedClause.
190 nsresult AppendClauseToPendingComposition(uint32_t aLength,
191 TextRangeType aTextRangeType)
193 return mPendingComposition.AppendClause(aLength, aTextRangeType);
197 * SetCaretInPendingComposition() sets caret position in the pending
198 * composition string and its length. This is optional. If IME doesn't
199 * want to show caret, it shouldn't need to call this.
201 * @param aOffset Offset of the caret in the pending composition
202 * string. This should not be larger than the length
203 * of the pending composition string.
204 * @param aLength Caret width. If this is 0, caret will be collapsed.
205 * Note that Gecko doesn't supported wide caret yet,
206 * therefore, this is ignored for now.
208 nsresult SetCaretInPendingComposition(uint32_t aOffset,
209 uint32_t aLength)
211 return mPendingComposition.SetCaret(aOffset, aLength);
215 * SetPendingComposition() is useful if native IME handler already creates
216 * array of clauses and/or caret information.
218 * @param aString Composition string. This may include native line
219 * breakers since they will be replaced with XP line
220 * breakers automatically.
221 * @param aRanges This should include the ranges of clauses and/or
222 * a range of caret. Note that this method allows
223 * some ranges overlap each other and the range order
224 * is not from start to end.
226 nsresult SetPendingComposition(const nsAString& aString,
227 const TextRangeArray* aRanges)
229 return mPendingComposition.Set(aString, aRanges);
233 * FlushPendingComposition() sends the pending composition string
234 * to the widget of the store DOM window. Before calling this, IME needs to
235 * set pending composition string with SetPendingCompositionString(),
236 * AppendClauseToPendingComposition() and/or
237 * SetCaretInPendingComposition().
239 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
240 * be initialized with this. Otherwise, initialized
241 * with the time at initializing.
243 nsresult FlushPendingComposition(nsEventStatus& aStatus,
244 const WidgetEventTime* aEventTime = nullptr)
246 return mPendingComposition.Flush(this, aStatus, aEventTime);
250 * ClearPendingComposition() makes this instance forget pending composition.
252 void ClearPendingComposition()
254 mPendingComposition.Clear();
258 * GetPendingCompositionClauses() returns text ranges which was appended by
259 * AppendClauseToPendingComposition() or SetPendingComposition().
261 const TextRangeArray* GetPendingCompositionClauses() const
263 return mPendingComposition.GetClauses();
267 * @see nsIWidget::NotifyIME()
269 nsresult NotifyIME(const IMENotification& aIMENotification);
272 * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
274 * @param aMessage Must be eKeyDown or eKeyUp.
275 * Use MaybeDispatchKeypressEvents() for dispatching
276 * eKeyPress.
277 * @param aKeyboardEvent A keyboard event.
278 * @param aStatus If dispatching event should be marked as consumed,
279 * set nsEventStatus_eConsumeNoDefault. Otherwise,
280 * set nsEventStatus_eIgnore. After dispatching
281 * a event and it's consumed this returns
282 * nsEventStatus_eConsumeNoDefault.
283 * @param aData Calling this method may cause calling
284 * WillDispatchKeyboardEvent() of the listener.
285 * aData will be set to its argument.
286 * @return true if an event is dispatched. Otherwise, false.
288 bool DispatchKeyboardEvent(EventMessage aMessage,
289 const WidgetKeyboardEvent& aKeyboardEvent,
290 nsEventStatus& aStatus,
291 void* aData = nullptr);
294 * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
295 * generated from aKeydownEvent.
297 * @param aKeyboardEvent A keyboard event.
298 * @param aStatus Sets the result when the caller dispatches
299 * aKeyboardEvent. Note that if the value is
300 * nsEventStatus_eConsumeNoDefault, this does NOT
301 * dispatch keypress events.
302 * When this method dispatches one or more keypress
303 * events and one of them is consumed, this returns
304 * nsEventStatus_eConsumeNoDefault.
305 * @param aData Calling this method may cause calling
306 * WillDispatchKeyboardEvent() of the listener.
307 * aData will be set to its argument.
308 * @param aNeedsCallback Set true when caller needs to initialize each
309 * eKeyPress event immediately before dispatch.
310 * Then, WillDispatchKeyboardEvent() is always called.
311 * @return true if one or more events are dispatched.
312 * Otherwise, false.
314 bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
315 nsEventStatus& aStatus,
316 void* aData = nullptr,
317 bool aNeedsCallback = false);
319 private:
320 // mWidget is owner of the instance. When this is created, this is set.
321 // And when mWidget is released, this is cleared by OnDestroyWidget().
322 // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
323 // return true).
324 nsIWidget* mWidget;
325 // mListener is a weak reference to TextEventDispatcherListener. That might
326 // be referred by JS. Therefore, the listener might be difficult to release
327 // itself if this is a strong reference. Additionally, it's difficult to
328 // check if a method to uninstall the listener is called by valid instance.
329 // So, using weak reference is the best way in this case.
330 nsWeakPtr mListener;
331 // mIMENotificationRequests should store current IME's notification requests.
332 // So, this may be invalid when IME doesn't have focus.
333 IMENotificationRequests mIMENotificationRequests;
335 // mPendingComposition stores new composition string temporarily.
336 // These values will be used for dispatching eCompositionChange event
337 // in Flush(). When Flush() is called, the members will be cleared
338 // automatically.
339 class PendingComposition
341 public:
342 PendingComposition();
343 nsresult SetString(const nsAString& aString);
344 nsresult AppendClause(uint32_t aLength, TextRangeType aTextRangeType);
345 nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
346 nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
347 nsresult Flush(TextEventDispatcher* aDispatcher,
348 nsEventStatus& aStatus,
349 const WidgetEventTime* aEventTime);
350 const TextRangeArray* GetClauses() const { return mClauses; }
351 void Clear();
353 private:
354 nsString mString;
355 RefPtr<TextRangeArray> mClauses;
356 TextRange mCaret;
357 bool mReplacedNativeLineBreakers;
359 void EnsureClauseArray();
362 * ReplaceNativeLineBreakers() replaces "\r\n" and "\r" to "\n" and adjust
363 * each clause information and the caret information.
365 void ReplaceNativeLineBreakers();
368 * AdjustRange() adjusts aRange as in the string with XP line breakers.
370 * @param aRange The reference to a range in aNativeString.
371 * This will be modified.
372 * @param aNativeString The string with native line breakers.
373 * This may include "\r\n" and/or "\r".
375 static void AdjustRange(TextRange& aRange, const nsAString& aNativeString);
377 PendingComposition mPendingComposition;
379 // While dispatching an event, this is incremented.
380 uint16_t mDispatchingEvent;
382 enum InputTransactionType : uint8_t
384 // No input transaction has been started.
385 eNoInputTransaction,
386 // Input transaction for native IME or keyboard event handler. Note that
387 // keyboard events may be dispatched via parent process if there is.
388 // In remote processes, this is also used when events come from the parent
389 // process and are not for tests because we cannot distinguish if
390 // TextEventDispatcher has which type of transaction when it dispatches
391 // (eNativeInputTransaction or eSameProcessSyncInputTransaction).
392 eNativeInputTransaction,
393 // Input transaction for automated tests which are APZ-aware. Note that
394 // keyboard events may be dispatched via parent process if there is.
395 eAsyncTestInputTransaction,
396 // Input transaction for automated tests which assume events are fired
397 // synchronously. I.e., keyboard events are always dispatched in the
398 // current process.
399 // In remote processes, this is also used when events come from the parent
400 // process and are not dispatched by the instance itself for APZ-aware
401 // tests because this instance won't dispatch the events via the parent
402 // process again.
403 eSameProcessSyncTestInputTransaction,
404 // Input transaction for Others (must be IME on B2G). Events are fired
405 // synchronously because TextInputProcessor which is the only user of
406 // this input transaction type supports only keyboard apps on B2G.
407 // Keyboard apps on B2G doesn't want to dispatch keyboard events to
408 // chrome process. Therefore, this should dispatch key events only in
409 // the current process.
410 eSameProcessSyncInputTransaction
413 InputTransactionType mInputTransactionType;
415 bool IsForTests() const
417 return mInputTransactionType == eAsyncTestInputTransaction ||
418 mInputTransactionType == eSameProcessSyncTestInputTransaction;
421 // ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should
422 // be dispatched via its parent process (if there is) for APZ. Otherwise,
423 // when the input transaction is for IME of B2G or automated tests which
424 // isn't APZ-aware, WidgetInputEvent should be dispatched form current
425 // process directly.
426 bool ShouldSendInputEventToAPZ() const
428 switch (mInputTransactionType) {
429 case eNativeInputTransaction:
430 case eAsyncTestInputTransaction:
431 return true;
432 case eSameProcessSyncTestInputTransaction:
433 case eSameProcessSyncInputTransaction:
434 return false;
435 case eNoInputTransaction:
436 NS_WARNING("Why does the caller need to dispatch an event when "
437 "there is no input transaction?");
438 return true;
439 default:
440 MOZ_CRASH("Define the behavior of new InputTransactionType");
444 // See IsComposing().
445 bool mIsComposing;
447 // See IsHandlingComposition().
448 bool mIsHandlingComposition;
450 // true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not
451 // received yet. Otherwise, false.
452 bool mHasFocus;
454 // If this is true, keydown and keyup events are dispatched even when there
455 // is a composition.
456 static bool sDispatchKeyEventsDuringComposition;
457 // If this is true, keypress events for non-printable keys are dispatched only
458 // for event listeners of the system event group in web content.
459 static bool sDispatchKeyPressEventsOnlySystemGroupInContent;
461 nsresult BeginInputTransactionInternal(
462 TextEventDispatcherListener* aListener,
463 InputTransactionType aType);
466 * InitEvent() initializes aEvent. This must be called before dispatching
467 * the event.
469 void InitEvent(WidgetGUIEvent& aEvent) const;
473 * DispatchEvent() dispatches aEvent on aWidget.
475 nsresult DispatchEvent(nsIWidget* aWidget,
476 WidgetGUIEvent& aEvent,
477 nsEventStatus& aStatus);
480 * DispatchInputEvent() dispatches aEvent on aWidget.
482 nsresult DispatchInputEvent(nsIWidget* aWidget,
483 WidgetInputEvent& aEvent,
484 nsEventStatus& aStatus);
487 * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
488 * been started it yet.
490 * @param aStatus If it succeeded to start composition normally, this
491 * returns nsEventStatus_eIgnore. Otherwise, e.g.,
492 * the composition is canceled during dispatching
493 * compositionstart event, this returns
494 * nsEventStatus_eConsumeNoDefault. In this case,
495 * the caller shouldn't keep doing its job.
496 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
497 * be initialized with this. Otherwise, initialized
498 * with the time at initializing.
499 * @return Only when something unexpected occurs, this returns
500 * an error. Otherwise, returns NS_OK even if aStatus
501 * is nsEventStatus_eConsumeNoDefault.
503 nsresult StartCompositionAutomaticallyIfNecessary(
504 nsEventStatus& aStatus,
505 const WidgetEventTime* aEventTime);
508 * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
510 * @param aMessage Must be eKeyDown, eKeyUp or eKeyPress.
511 * @param aKeyboardEvent A keyboard event. If aMessage is eKeyPress and
512 * the event is for second or later character, its
513 * mKeyValue should be empty string.
514 * @param aStatus If dispatching event should be marked as consumed,
515 * set nsEventStatus_eConsumeNoDefault. Otherwise,
516 * set nsEventStatus_eIgnore. After dispatching
517 * a event and it's consumed this returns
518 * nsEventStatus_eConsumeNoDefault.
519 * @param aData Calling this method may cause calling
520 * WillDispatchKeyboardEvent() of the listener.
521 * aData will be set to its argument.
522 * @param aIndexOfKeypress This must be 0 if aMessage isn't eKeyPress or
523 * aKeyboard.mKeyNameIndex isn't
524 * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
525 * when an eKeyPress event causes inputting
526 * text, this must be between 0 and
527 * mKeyValue.Length() - 1 since keypress events
528 * sending only one character per event.
529 * @param aNeedsCallback Set true when caller needs to initialize each
530 * eKeyPress event immediately before dispatch.
531 * Then, WillDispatchKeyboardEvent() is always called.
532 * @return true if an event is dispatched. Otherwise, false.
534 bool DispatchKeyboardEventInternal(EventMessage aMessage,
535 const WidgetKeyboardEvent& aKeyboardEvent,
536 nsEventStatus& aStatus,
537 void* aData,
538 uint32_t aIndexOfKeypress = 0,
539 bool aNeedsCallback = false);
542 * ClearNotificationRequests() clears mIMENotificationRequests.
544 void ClearNotificationRequests();
547 * UpdateNotificationRequests() updates mIMENotificationRequests with
548 * current state. If the instance doesn't have focus, this clears
549 * mIMENotificationRequests. Otherwise, updates it with both requests of
550 * current listener and native listener.
552 void UpdateNotificationRequests();
555 } // namespace widget
556 } // namespace mozilla
558 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_