Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / events / TextComposition.h
blobbc1eba55b5167e394392c0f5a2ef3746ac7cd71a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_TextComposition_h
8 #define mozilla_TextComposition_h
10 #include "nsCOMPtr.h"
11 #include "nsINode.h"
12 #include "nsIWidget.h"
13 #include "nsTArray.h"
14 #include "nsThreadUtils.h"
15 #include "nsPresContext.h"
16 #include "mozilla/AlreadyAddRefed.h"
17 #include "mozilla/Attributes.h"
18 #include "mozilla/EventForwards.h"
19 #include "mozilla/RangeBoundary.h"
20 #include "mozilla/TextRange.h"
21 #include "mozilla/dom/BrowserParent.h"
22 #include "mozilla/dom/Text.h"
24 class nsRange;
26 struct CharacterDataChangeInfo;
28 namespace mozilla {
30 class EditorBase;
31 class EventDispatchingCallback;
32 class IMEStateManager;
34 /**
35 * TextComposition represents a text composition. This class stores the
36 * composition event target and its presContext. At dispatching the event via
37 * this class, the instances use the stored event target.
40 class TextComposition final {
41 friend class IMEStateManager;
43 NS_INLINE_DECL_REFCOUNTING(TextComposition)
45 public:
46 typedef dom::BrowserParent BrowserParent;
47 typedef dom::Text Text;
49 static bool IsHandlingSelectionEvent() { return sHandlingSelectionEvent; }
51 TextComposition(nsPresContext* aPresContext, nsINode* aNode,
52 BrowserParent* aBrowserParent,
53 WidgetCompositionEvent* aCompositionEvent);
54 TextComposition() = delete;
55 TextComposition(const TextComposition& aOther) = delete;
57 bool Destroyed() const { return !mPresContext; }
58 nsPresContext* GetPresContext() const { return mPresContext; }
59 nsINode* GetEventTargetNode() const { return mNode; }
60 // The text node which includes composition string.
61 Text* GetContainerTextNode() const { return mContainerTextNode; }
62 // The latest CompositionEvent.data value except compositionstart event.
63 // This value is modified at dispatching compositionupdate.
64 const nsString& LastData() const { return mLastData; }
65 // Returns commit string if it'll be commited as-is.
66 nsString CommitStringIfCommittedAsIs() const;
67 // The composition string which is already handled by the focused editor.
68 // I.e., this value must be same as the composition string on the focused
69 // editor. This value is modified at a call of
70 // EditorDidHandleCompositionChangeEvent().
71 // Note that mString and mLastData are different between dispatcing
72 // compositionupdate and compositionchange event handled by focused editor.
73 const nsString& String() const { return mString; }
74 // The latest clauses range of the composition string.
75 // During compositionupdate event, GetRanges() returns old ranges.
76 // So if getting on compositionupdate, Use GetLastRange instead of GetRange().
77 TextRangeArray* GetLastRanges() const { return mLastRanges; }
78 // Returns the clauses and/or caret range of the composition string.
79 // This is modified at a call of EditorWillHandleCompositionChangeEvent().
80 // This may return null if there is no clauses and caret.
81 // XXX We should return |const TextRangeArray*| here, but it causes compile
82 // error due to inaccessible Release() method.
83 TextRangeArray* GetRanges() const { return mRanges; }
84 // Returns the widget which is proper to call NotifyIME().
85 already_AddRefed<nsIWidget> GetWidget() const {
86 if (!mPresContext) {
87 return nullptr;
89 return do_AddRef(mPresContext->GetRootWidget());
91 // Returns the tab parent which has this composition in its remote process.
92 BrowserParent* GetBrowserParent() const { return mBrowserParent; }
93 // Returns true if the composition is started with synthesized event which
94 // came from nsDOMWindowUtils.
95 bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; }
97 // Returns the composition ID. It must be 0 if the composition is synthesized
98 // in a content process. Otherwise, returns 1 or larger value.
99 uint32_t Id() const { return mCompositionId; }
101 const widget::NativeIMEContext& GetNativeIMEContext() const {
102 return mNativeContext;
106 * This is called when IMEStateManager stops managing the instance.
108 void Destroy();
111 * Request to commit (or cancel) the composition to IME. This method should
112 * be called only by IMEStateManager::NotifyIME().
114 nsresult RequestToCommit(nsIWidget* aWidget, bool aDiscard);
117 * IsRequestingCommitOrCancelComposition() returns true if the instance is
118 * requesting widget to commit or cancel composition.
120 bool IsRequestingCommitOrCancelComposition() const {
121 return mIsRequestingCancel || mIsRequestingCommit;
125 * Send a notification to IME. It depends on the IME or platform spec what
126 * will occur (or not occur).
128 nsresult NotifyIME(widget::IMEMessage aMessage);
131 * the offset of first composition string
133 uint32_t NativeOffsetOfStartComposition() const {
134 return mCompositionStartOffset;
138 * the offset of first selected clause or start of composition
140 uint32_t NativeOffsetOfTargetClause() const {
141 return mCompositionStartOffset + mTargetClauseOffsetInComposition;
145 * Return current composition start and end point in the DOM tree.
146 * Note that one of or both of those result container may be different
147 * from GetContainerTextNode() if the DOM tree was modified by the web
148 * app. If there is no composition string the DOM tree, these return
149 * unset range boundaries.
151 RawRangeBoundary FirstIMESelectionStartRef() const;
152 RawRangeBoundary LastIMESelectionEndRef() const;
155 * The offset of composition string in the text node. If composition string
156 * hasn't been inserted in any text node yet, this returns UINT32_MAX.
158 uint32_t XPOffsetInTextNode() const {
159 return mCompositionStartOffsetInTextNode;
163 * The length of composition string in the text node. If composition string
164 * hasn't been inserted in any text node yet, this returns 0.
166 uint32_t XPLengthInTextNode() const {
167 return mCompositionLengthInTextNode == UINT32_MAX
169 : mCompositionLengthInTextNode;
173 * The end offset of composition string in the text node. If composition
174 * string hasn't been inserted in any text node yet, this returns UINT32_MAX.
176 uint32_t XPEndOffsetInTextNode() const {
177 if (mCompositionStartOffsetInTextNode == UINT32_MAX ||
178 mCompositionLengthInTextNode == UINT32_MAX) {
179 return UINT32_MAX;
181 return mCompositionStartOffsetInTextNode + mCompositionLengthInTextNode;
185 * Returns true if there is non-empty composition string and it's not fixed.
186 * Otherwise, false.
188 bool IsComposing() const { return mIsComposing; }
191 * Returns true while editor is handling an event which is modifying the
192 * composition string.
194 bool IsEditorHandlingEvent() const { return mIsEditorHandlingEvent; }
197 * IsMovingToNewTextNode() returns true if editor detects the text node
198 * has been removed and still not insert the composition string into
199 * new text node.
201 bool IsMovingToNewTextNode() const {
202 return !mContainerTextNode && mCompositionLengthInTextNode &&
203 mCompositionLengthInTextNode != UINT32_MAX;
207 * StartHandlingComposition() and EndHandlingComposition() are called by
208 * editor when it holds a TextComposition instance and release it.
210 void StartHandlingComposition(EditorBase* aEditorBase);
211 void EndHandlingComposition(EditorBase* aEditorBase);
214 * OnEditorDestroyed() is called when the editor is destroyed but there is
215 * active composition.
217 void OnEditorDestroyed();
220 * CompositionChangeEventHandlingMarker class should be created at starting
221 * to handle text event in focused editor. This calls
222 * EditorWillHandleCompositionChangeEvent() and
223 * EditorDidHandleCompositionChangeEvent() automatically.
225 class MOZ_STACK_CLASS CompositionChangeEventHandlingMarker {
226 public:
227 CompositionChangeEventHandlingMarker(
228 TextComposition* aComposition,
229 const WidgetCompositionEvent* aCompositionChangeEvent)
230 : mComposition(aComposition) {
231 mComposition->EditorWillHandleCompositionChangeEvent(
232 aCompositionChangeEvent);
235 ~CompositionChangeEventHandlingMarker() {
236 mComposition->EditorDidHandleCompositionChangeEvent();
239 private:
240 RefPtr<TextComposition> mComposition;
241 CompositionChangeEventHandlingMarker();
242 CompositionChangeEventHandlingMarker(
243 const CompositionChangeEventHandlingMarker& aOther);
247 * OnUpdateCompositionInEditor() is called when editor updates composition
248 * string in the DOM tree.
250 * @param aStringToInsert The string to insert the text node actually.
251 * This may be different from the data of
252 * dispatching composition event because it may
253 * be replaced with different character for
254 * passwords, or truncated due to maxlength.
255 * @param aTextNode The text node which includes composition string.
256 * @param aOffset The offset of composition string in aTextNode.
258 void OnUpdateCompositionInEditor(const nsAString& aStringToInsert,
259 Text& aTextNode, uint32_t aOffset) {
260 mContainerTextNode = &aTextNode;
261 mCompositionStartOffsetInTextNode = aOffset;
262 NS_WARNING_ASSERTION(mCompositionStartOffsetInTextNode != UINT32_MAX,
263 "The text node is really too long.");
264 mCompositionLengthInTextNode = aStringToInsert.Length();
265 NS_WARNING_ASSERTION(mCompositionLengthInTextNode != UINT32_MAX,
266 "The string to insert is really too long.");
270 * OnTextNodeRemoved() is called when focused editor is reframed and
271 * mContainerTextNode may be (or have been) replaced with different text
272 * node, or just removes the text node due to empty.
274 void OnTextNodeRemoved() {
275 mContainerTextNode = nullptr;
276 // Don't reset mCompositionStartOffsetInTextNode nor
277 // mCompositionLengthInTextNode because editor needs them to restore
278 // composition in new text node.
282 * OnCharacterDataChanged() is called when IMEContentObserver receives
283 * character data change notifications.
285 void OnCharacterDataChanged(Text& aText,
286 const CharacterDataChangeInfo& aInfo);
288 private:
289 // Private destructor, to discourage deletion outside of Release():
290 ~TextComposition() {
291 // WARNING: mPresContext may be destroying, so, be careful if you touch it.
294 // sHandlingSelectionEvent is true while TextComposition sends a selection
295 // event to ContentEventHandler.
296 static bool sHandlingSelectionEvent;
298 // This class holds nsPresContext weak. This instance shouldn't block
299 // destroying it. When the presContext is being destroyed, it's notified to
300 // IMEStateManager::OnDestroyPresContext(), and then, it destroy
301 // this instance.
302 nsPresContext* mPresContext;
303 RefPtr<nsINode> mNode;
304 RefPtr<BrowserParent> mBrowserParent;
306 // The text node which includes the composition string.
307 RefPtr<Text> mContainerTextNode;
309 // This is the clause and caret range information which is managed by
310 // the focused editor. This may be null if there is no clauses or caret.
311 RefPtr<TextRangeArray> mRanges;
312 // Same as mRange, but mRange will have old data during compositionupdate.
313 // So this will be valied during compositionupdate.
314 RefPtr<TextRangeArray> mLastRanges;
316 // mNativeContext stores a opaque pointer. This works as the "ID" for this
317 // composition. Don't access the instance, it may not be available.
318 widget::NativeIMEContext mNativeContext;
320 // mEditorBaseWeak is a weak reference to the focused editor handling
321 // composition.
322 nsWeakPtr mEditorBaseWeak;
324 // mLastData stores the data attribute of the latest composition event (except
325 // the compositionstart event).
326 nsString mLastData;
328 // mString stores the composition text which has been handled by the focused
329 // editor.
330 nsString mString;
332 // Composition ID of this composition. If this is in a parent process,
333 // this is 1 or larger. If the composition is created for managing a
334 // composition synthesized in a content process, this is 0.
335 const uint32_t mCompositionId = 0;
337 // Offset of the composition string from start of the editor
338 uint32_t mCompositionStartOffset;
339 // Offset of the selected clause of the composition string from
340 // mCompositionStartOffset
341 uint32_t mTargetClauseOffsetInComposition;
342 // Offset of the composition string in mContainerTextNode.
343 // NOTE: This is NOT valid in the main process if focused editor is in a
344 // remote process.
345 uint32_t mCompositionStartOffsetInTextNode;
346 // Length of the composition string in mContainerTextNode. If this instance
347 // has already dispatched eCompositionCommit(AsIs) and
348 // EditorDidHandleCompositionChangeEvent() has already been called,
349 // this may be different from length of mString because committed string
350 // may be truncated by maxlength attribute of <input> or <textarea>.
351 // NOTE: This is NOT valid in the main process if focused editor is in a
352 // remote process.
353 uint32_t mCompositionLengthInTextNode;
355 // See the comment for IsSynthesizedForTests().
356 bool mIsSynthesizedForTests;
358 // See the comment for IsComposing().
359 bool mIsComposing;
361 // mIsEditorHandlingEvent is true while editor is modifying the composition
362 // string.
363 bool mIsEditorHandlingEvent;
365 // mIsRequestingCommit or mIsRequestingCancel is true *only* while we're
366 // requesting commit or canceling the composition. In other words, while
367 // one of these values is true, we're handling the request.
368 bool mIsRequestingCommit;
369 bool mIsRequestingCancel;
371 // mRequestedToCommitOrCancel is true *after* we requested IME to commit or
372 // cancel the composition. In other words, we already requested of IME that
373 // it commits or cancels current composition.
374 // NOTE: Before this is set to true, both mIsRequestingCommit and
375 // mIsRequestingCancel are set to false.
376 bool mRequestedToCommitOrCancel;
378 // Set to true if the instance dispatches an eCompositionChange event.
379 bool mHasDispatchedDOMTextEvent;
381 // Before this dispatches commit event into the tree, this is set to true.
382 // So, this means if native IME already commits the composition.
383 bool mHasReceivedCommitEvent;
385 // mWasNativeCompositionEndEventDiscarded is true if this composition was
386 // requested commit or cancel itself but native compositionend event is
387 // discarded by PresShell due to not safe to dispatch events.
388 bool mWasNativeCompositionEndEventDiscarded;
390 // Allow control characters appear in composition string.
391 // When this is false, control characters except
392 // CHARACTER TABULATION (horizontal tab) are removed from
393 // both composition string and data attribute of compositionupdate
394 // and compositionend events.
395 bool mAllowControlCharacters;
397 // mWasCompositionStringEmpty is true if the composition string was empty
398 // when DispatchCompositionEvent() is called.
399 bool mWasCompositionStringEmpty;
402 * If we're requesting IME to commit or cancel composition, or we've already
403 * requested it, or we've already known this composition has been ended in
404 * IME, we don't need to request commit nor cancel composition anymore and
405 * shouldn't do so if we're in content process for not committing/canceling
406 * "current" composition in native IME. So, when this returns true,
407 * RequestIMEToCommit() does nothing.
409 bool CanRequsetIMEToCommitOrCancelComposition() const {
410 return !mIsRequestingCommit && !mIsRequestingCancel &&
411 !mRequestedToCommitOrCancel && !mHasReceivedCommitEvent;
415 * GetEditorBase() returns EditorBase pointer of mEditorBaseWeak.
417 already_AddRefed<EditorBase> GetEditorBase() const;
420 * HasEditor() returns true if mEditorBaseWeak holds EditorBase instance
421 * which is alive. Otherwise, false.
423 bool HasEditor() const;
426 * EditorWillHandleCompositionChangeEvent() must be called before the focused
427 * editor handles the compositionchange event.
429 void EditorWillHandleCompositionChangeEvent(
430 const WidgetCompositionEvent* aCompositionChangeEvent);
433 * EditorDidHandleCompositionChangeEvent() must be called after the focused
434 * editor handles a compositionchange event.
436 void EditorDidHandleCompositionChangeEvent();
439 * IsValidStateForComposition() returns true if it's safe to dispatch an event
440 * to the DOM tree. Otherwise, false.
441 * WARNING: This doesn't check script blocker state. It should be checked
442 * before dispatching the first event.
444 bool IsValidStateForComposition(nsIWidget* aWidget) const;
447 * DispatchCompositionEvent() dispatches the aCompositionEvent to the mContent
448 * synchronously. The caller must ensure that it's safe to dispatch the event.
450 MOZ_CAN_RUN_SCRIPT void DispatchCompositionEvent(
451 WidgetCompositionEvent* aCompositionEvent, nsEventStatus* aStatus,
452 EventDispatchingCallback* aCallBack, bool aIsSynthesized);
455 * Simply calling EventDispatcher::Dispatch() with plugin event.
456 * If dispatching event has no orginal clone, aOriginalEvent can be null.
458 MOZ_CAN_RUN_SCRIPT void DispatchEvent(
459 WidgetCompositionEvent* aDispatchEvent, nsEventStatus* aStatus,
460 EventDispatchingCallback* aCallback,
461 const WidgetCompositionEvent* aOriginalEvent = nullptr);
464 * HandleSelectionEvent() sends the selection event to ContentEventHandler
465 * or dispatches it to the focused child process.
467 MOZ_CAN_RUN_SCRIPT
468 void HandleSelectionEvent(WidgetSelectionEvent* aSelectionEvent) {
469 RefPtr<nsPresContext> presContext(mPresContext);
470 RefPtr<BrowserParent> browserParent(mBrowserParent);
471 HandleSelectionEvent(presContext, browserParent, aSelectionEvent);
473 MOZ_CAN_RUN_SCRIPT
474 static void HandleSelectionEvent(nsPresContext* aPresContext,
475 BrowserParent* aBrowserParent,
476 WidgetSelectionEvent* aSelectionEvent);
479 * MaybeDispatchCompositionUpdate() may dispatch a compositionupdate event
480 * if aCompositionEvent changes composition string.
481 * @return Returns false if dispatching the compositionupdate event caused
482 * destroying this composition.
484 MOZ_CAN_RUN_SCRIPT bool MaybeDispatchCompositionUpdate(
485 const WidgetCompositionEvent* aCompositionEvent);
488 * CloneAndDispatchAs() dispatches a composition event which is
489 * duplicateed from aCompositionEvent and set the aMessage.
491 * @return Returns BaseEventFlags which is the result of dispatched event.
493 MOZ_CAN_RUN_SCRIPT BaseEventFlags
494 CloneAndDispatchAs(const WidgetCompositionEvent* aCompositionEvent,
495 EventMessage aMessage, nsEventStatus* aStatus = nullptr,
496 EventDispatchingCallback* aCallBack = nullptr);
499 * If IME has already dispatched compositionend event but it was discarded
500 * by PresShell due to not safe to dispatch, this returns true.
502 bool WasNativeCompositionEndEventDiscarded() const {
503 return mWasNativeCompositionEndEventDiscarded;
507 * OnCompositionEventDiscarded() is called when PresShell discards
508 * compositionupdate, compositionend or compositionchange event due to not
509 * safe to dispatch event.
511 void OnCompositionEventDiscarded(WidgetCompositionEvent* aCompositionEvent);
514 * OnCompositionEventDispatched() is called after a composition event is
515 * dispatched.
517 MOZ_CAN_RUN_SCRIPT void OnCompositionEventDispatched(
518 const WidgetCompositionEvent* aDispatchEvent);
521 * MaybeNotifyIMEOfCompositionEventHandled() notifies IME of composition
522 * event handled. This should be called after dispatching a composition
523 * event which came from widget.
525 void MaybeNotifyIMEOfCompositionEventHandled(
526 const WidgetCompositionEvent* aCompositionEvent);
529 * GetSelectionStartOffset() returns normal selection start offset in the
530 * editor which has this composition.
531 * If it failed or lost focus, this would return 0.
533 MOZ_CAN_RUN_SCRIPT uint32_t GetSelectionStartOffset();
536 * OnStartOffsetUpdatedInChild() is called when composition start offset
537 * is updated in the child process. I.e., this is called and never called
538 * if the composition is in this process.
539 * @param aStartOffset New composition start offset with native
540 * linebreaks.
542 void OnStartOffsetUpdatedInChild(uint32_t aStartOffset);
545 * CompositionEventDispatcher dispatches the specified composition (or text)
546 * event.
548 class CompositionEventDispatcher : public Runnable {
549 public:
550 CompositionEventDispatcher(TextComposition* aTextComposition,
551 nsINode* aEventTarget,
552 EventMessage aEventMessage,
553 const nsAString& aData,
554 bool aIsSynthesizedEvent = false);
555 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
557 private:
558 RefPtr<TextComposition> mTextComposition;
559 nsCOMPtr<nsINode> mEventTarget;
560 nsString mData;
561 EventMessage mEventMessage;
562 bool mIsSynthesizedEvent;
564 CompositionEventDispatcher()
565 : Runnable("TextComposition::CompositionEventDispatcher"),
566 mEventMessage(eVoidEvent),
567 mIsSynthesizedEvent(false){};
571 * DispatchCompositionEventRunnable() dispatches a composition event to the
572 * content. Be aware, if you use this method, nsPresShellEventCB isn't used.
573 * That means that nsIFrame::HandleEvent() is never called.
574 * WARNING: The instance which is managed by IMEStateManager may be
575 * destroyed by this method call.
577 * @param aEventMessage Must be one of composition events.
578 * @param aData Used for mData value.
579 * @param aIsSynthesizingCommit true if this is called for synthesizing
580 * commit or cancel composition. Otherwise,
581 * false.
583 void DispatchCompositionEventRunnable(EventMessage aEventMessage,
584 const nsAString& aData,
585 bool aIsSynthesizingCommit = false);
589 * TextCompositionArray manages the instances of TextComposition class.
590 * Managing with array is enough because only one composition is typically
591 * there. Even if user switches native IME context, it's very rare that
592 * second or more composition is started.
593 * It's assumed that this is used by IMEStateManager for storing all active
594 * compositions in the process. If the instance is it, each TextComposition
595 * in the array can be destroyed by calling some methods of itself.
598 class TextCompositionArray final
599 : public AutoTArray<RefPtr<TextComposition>, 2> {
600 public:
601 // Looking for per native IME context.
602 index_type IndexOf(const widget::NativeIMEContext& aNativeIMEContext);
603 index_type IndexOf(nsIWidget* aWidget);
605 TextComposition* GetCompositionFor(nsIWidget* aWidget);
606 TextComposition* GetCompositionFor(
607 const WidgetCompositionEvent* aCompositionEvent);
609 // Looking for per nsPresContext
610 index_type IndexOf(nsPresContext* aPresContext);
611 index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode);
613 TextComposition* GetCompositionFor(nsPresContext* aPresContext);
614 TextComposition* GetCompositionFor(nsPresContext* aPresContext,
615 nsINode* aNode);
616 TextComposition* GetCompositionInContent(nsPresContext* aPresContext,
617 nsIContent* aContent);
620 } // namespace mozilla
622 #endif // #ifndef mozilla_TextComposition_h