From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- dom/events/TextComposition.h | 631 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 631 insertions(+) create mode 100644 dom/events/TextComposition.h (limited to 'dom/events/TextComposition.h') diff --git a/dom/events/TextComposition.h b/dom/events/TextComposition.h new file mode 100644 index 0000000000..7199171068 --- /dev/null +++ b/dom/events/TextComposition.h @@ -0,0 +1,631 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_TextComposition_h +#define mozilla_TextComposition_h + +#include "nsCOMPtr.h" +#include "nsINode.h" +#include "nsIWidget.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "nsPresContext.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/Attributes.h" +#include "mozilla/EventForwards.h" +#include "mozilla/RangeBoundary.h" +#include "mozilla/TextRange.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/Text.h" + +class nsRange; + +struct CharacterDataChangeInfo; + +namespace mozilla { + +class EditorBase; +class EventDispatchingCallback; +class IMEStateManager; + +/** + * TextComposition represents a text composition. This class stores the + * composition event target and its presContext. At dispatching the event via + * this class, the instances use the stored event target. + */ + +class TextComposition final { + friend class IMEStateManager; + + NS_INLINE_DECL_REFCOUNTING(TextComposition) + + public: + typedef dom::BrowserParent BrowserParent; + typedef dom::Text Text; + + static bool IsHandlingSelectionEvent() { return sHandlingSelectionEvent; } + + TextComposition(nsPresContext* aPresContext, nsINode* aNode, + BrowserParent* aBrowserParent, + WidgetCompositionEvent* aCompositionEvent); + + bool Destroyed() const { return !mPresContext; } + nsPresContext* GetPresContext() const { return mPresContext; } + nsINode* GetEventTargetNode() const { return mNode; } + // The text node which includes composition string. + Text* GetContainerTextNode() const { return mContainerTextNode; } + // The latest CompositionEvent.data value except compositionstart event. + // This value is modified at dispatching compositionupdate. + const nsString& LastData() const { return mLastData; } + // Returns commit string if it'll be commited as-is. + nsString CommitStringIfCommittedAsIs() const; + // The composition string which is already handled by the focused editor. + // I.e., this value must be same as the composition string on the focused + // editor. This value is modified at a call of + // EditorDidHandleCompositionChangeEvent(). + // Note that mString and mLastData are different between dispatcing + // compositionupdate and compositionchange event handled by focused editor. + const nsString& String() const { return mString; } + // The latest clauses range of the composition string. + // During compositionupdate event, GetRanges() returns old ranges. + // So if getting on compositionupdate, Use GetLastRange instead of GetRange(). + TextRangeArray* GetLastRanges() const { return mLastRanges; } + // Returns the clauses and/or caret range of the composition string. + // This is modified at a call of EditorWillHandleCompositionChangeEvent(). + // This may return null if there is no clauses and caret. + // XXX We should return |const TextRangeArray*| here, but it causes compile + // error due to inaccessible Release() method. + TextRangeArray* GetRanges() const { return mRanges; } + // Returns the widget which is proper to call NotifyIME(). + already_AddRefed GetWidget() const { + if (!mPresContext) { + return nullptr; + } + return do_AddRef(mPresContext->GetRootWidget()); + } + // Returns the tab parent which has this composition in its remote process. + BrowserParent* GetBrowserParent() const { return mBrowserParent; } + // Returns true if the composition is started with synthesized event which + // came from nsDOMWindowUtils. + bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; } + + const widget::NativeIMEContext& GetNativeIMEContext() const { + return mNativeContext; + } + + /** + * This is called when IMEStateManager stops managing the instance. + */ + void Destroy(); + + /** + * Request to commit (or cancel) the composition to IME. This method should + * be called only by IMEStateManager::NotifyIME(). + */ + nsresult RequestToCommit(nsIWidget* aWidget, bool aDiscard); + + /** + * IsRequestingCommitOrCancelComposition() returns true if the instance is + * requesting widget to commit or cancel composition. + */ + bool IsRequestingCommitOrCancelComposition() const { + return mIsRequestingCancel || mIsRequestingCommit; + } + + /** + * Send a notification to IME. It depends on the IME or platform spec what + * will occur (or not occur). + */ + nsresult NotifyIME(widget::IMEMessage aMessage); + + /** + * the offset of first composition string + */ + uint32_t NativeOffsetOfStartComposition() const { + return mCompositionStartOffset; + } + + /** + * the offset of first selected clause or start of composition + */ + uint32_t NativeOffsetOfTargetClause() const { + return mCompositionStartOffset + mTargetClauseOffsetInComposition; + } + + /** + * Return current composition start and end point in the DOM tree. + * Note that one of or both of those result container may be different + * from GetContainerTextNode() if the DOM tree was modified by the web + * app. If there is no composition string the DOM tree, these return + * unset range boundaries. + */ + RawRangeBoundary FirstIMESelectionStartRef() const; + RawRangeBoundary LastIMESelectionEndRef() const; + + /** + * The offset of composition string in the text node. If composition string + * hasn't been inserted in any text node yet, this returns UINT32_MAX. + */ + uint32_t XPOffsetInTextNode() const { + return mCompositionStartOffsetInTextNode; + } + + /** + * The length of composition string in the text node. If composition string + * hasn't been inserted in any text node yet, this returns 0. + */ + uint32_t XPLengthInTextNode() const { + return mCompositionLengthInTextNode == UINT32_MAX + ? 0 + : mCompositionLengthInTextNode; + } + + /** + * The end offset of composition string in the text node. If composition + * string hasn't been inserted in any text node yet, this returns UINT32_MAX. + */ + uint32_t XPEndOffsetInTextNode() const { + if (mCompositionStartOffsetInTextNode == UINT32_MAX || + mCompositionLengthInTextNode == UINT32_MAX) { + return UINT32_MAX; + } + return mCompositionStartOffsetInTextNode + mCompositionLengthInTextNode; + } + + /** + * Returns true if there is non-empty composition string and it's not fixed. + * Otherwise, false. + */ + bool IsComposing() const { return mIsComposing; } + + /** + * Returns true while editor is handling an event which is modifying the + * composition string. + */ + bool IsEditorHandlingEvent() const { return mIsEditorHandlingEvent; } + + /** + * IsMovingToNewTextNode() returns true if editor detects the text node + * has been removed and still not insert the composition string into + * new text node. + */ + bool IsMovingToNewTextNode() const { + return !mContainerTextNode && mCompositionLengthInTextNode && + mCompositionLengthInTextNode != UINT32_MAX; + } + + /** + * StartHandlingComposition() and EndHandlingComposition() are called by + * editor when it holds a TextComposition instance and release it. + */ + void StartHandlingComposition(EditorBase* aEditorBase); + void EndHandlingComposition(EditorBase* aEditorBase); + + /** + * OnEditorDestroyed() is called when the editor is destroyed but there is + * active composition. + */ + void OnEditorDestroyed(); + + /** + * CompositionChangeEventHandlingMarker class should be created at starting + * to handle text event in focused editor. This calls + * EditorWillHandleCompositionChangeEvent() and + * EditorDidHandleCompositionChangeEvent() automatically. + */ + class MOZ_STACK_CLASS CompositionChangeEventHandlingMarker { + public: + CompositionChangeEventHandlingMarker( + TextComposition* aComposition, + const WidgetCompositionEvent* aCompositionChangeEvent) + : mComposition(aComposition) { + mComposition->EditorWillHandleCompositionChangeEvent( + aCompositionChangeEvent); + } + + ~CompositionChangeEventHandlingMarker() { + mComposition->EditorDidHandleCompositionChangeEvent(); + } + + private: + RefPtr mComposition; + CompositionChangeEventHandlingMarker(); + CompositionChangeEventHandlingMarker( + const CompositionChangeEventHandlingMarker& aOther); + }; + + /** + * OnUpdateCompositionInEditor() is called when editor updates composition + * string in the DOM tree. + * + * @param aStringToInsert The string to insert the text node actually. + * This may be different from the data of + * dispatching composition event because it may + * be replaced with different character for + * passwords, or truncated due to maxlength. + * @param aTextNode The text node which includes composition string. + * @param aOffset The offset of composition string in aTextNode. + */ + void OnUpdateCompositionInEditor(const nsAString& aStringToInsert, + Text& aTextNode, uint32_t aOffset) { + mContainerTextNode = &aTextNode; + mCompositionStartOffsetInTextNode = aOffset; + NS_WARNING_ASSERTION(mCompositionStartOffsetInTextNode != UINT32_MAX, + "The text node is really too long."); + mCompositionLengthInTextNode = aStringToInsert.Length(); + NS_WARNING_ASSERTION(mCompositionLengthInTextNode != UINT32_MAX, + "The string to insert is really too long."); + } + + /** + * OnTextNodeRemoved() is called when focused editor is reframed and + * mContainerTextNode may be (or have been) replaced with different text + * node, or just removes the text node due to empty. + */ + void OnTextNodeRemoved() { + mContainerTextNode = nullptr; + // Don't reset mCompositionStartOffsetInTextNode nor + // mCompositionLengthInTextNode because editor needs them to restore + // composition in new text node. + } + + /** + * OnCharacterDataChanged() is called when IMEContentObserver receives + * character data change notifications. + */ + void OnCharacterDataChanged(Text& aText, + const CharacterDataChangeInfo& aInfo); + + private: + // Private destructor, to discourage deletion outside of Release(): + ~TextComposition() { + // WARNING: mPresContext may be destroying, so, be careful if you touch it. + } + + // sHandlingSelectionEvent is true while TextComposition sends a selection + // event to ContentEventHandler. + static bool sHandlingSelectionEvent; + + // This class holds nsPresContext weak. This instance shouldn't block + // destroying it. When the presContext is being destroyed, it's notified to + // IMEStateManager::OnDestroyPresContext(), and then, it destroy + // this instance. + nsPresContext* mPresContext; + RefPtr mNode; + RefPtr mBrowserParent; + + // The text node which includes the composition string. + RefPtr mContainerTextNode; + + // This is the clause and caret range information which is managed by + // the focused editor. This may be null if there is no clauses or caret. + RefPtr mRanges; + // Same as mRange, but mRange will have old data during compositionupdate. + // So this will be valied during compositionupdate. + RefPtr mLastRanges; + + // mNativeContext stores a opaque pointer. This works as the "ID" for this + // composition. Don't access the instance, it may not be available. + widget::NativeIMEContext mNativeContext; + + // mEditorBaseWeak is a weak reference to the focused editor handling + // composition. + nsWeakPtr mEditorBaseWeak; + + // mLastData stores the data attribute of the latest composition event (except + // the compositionstart event). + nsString mLastData; + + // mString stores the composition text which has been handled by the focused + // editor. + nsString mString; + + // Offset of the composition string from start of the editor + uint32_t mCompositionStartOffset; + // Offset of the selected clause of the composition string from + // mCompositionStartOffset + uint32_t mTargetClauseOffsetInComposition; + // Offset of the composition string in mContainerTextNode. + // NOTE: This is NOT valid in the main process if focused editor is in a + // remote process. + uint32_t mCompositionStartOffsetInTextNode; + // Length of the composition string in mContainerTextNode. If this instance + // has already dispatched eCompositionCommit(AsIs) and + // EditorDidHandleCompositionChangeEvent() has already been called, + // this may be different from length of mString because committed string + // may be truncated by maxlength attribute of or