michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_TextComposition_h michael@0: #define mozilla_TextComposition_h michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsINode.h" michael@0: #include "nsIWeakReference.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsTArray.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsPresContext.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/EventForwards.h" michael@0: #include "mozilla/TextRange.h" michael@0: michael@0: class nsIEditor; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class EventDispatchingCallback; michael@0: class IMEStateManager; michael@0: michael@0: /** michael@0: * TextComposition represents a text composition. This class stores the michael@0: * composition event target and its presContext. At dispatching the event via michael@0: * this class, the instances use the stored event target. michael@0: */ michael@0: michael@0: class TextComposition MOZ_FINAL michael@0: { michael@0: friend class IMEStateManager; michael@0: michael@0: NS_INLINE_DECL_REFCOUNTING(TextComposition) michael@0: michael@0: public: michael@0: TextComposition(nsPresContext* aPresContext, michael@0: nsINode* aNode, michael@0: WidgetGUIEvent* aEvent); michael@0: michael@0: bool Destroyed() const { return !mPresContext; } michael@0: nsPresContext* GetPresContext() const { return mPresContext; } michael@0: nsINode* GetEventTargetNode() const { return mNode; } michael@0: // The latest CompositionEvent.data value except compositionstart event. michael@0: // This value is modified at dispatching compositionupdate. michael@0: const nsString& LastData() const { return mLastData; } michael@0: // The composition string which is already handled by the focused editor. michael@0: // I.e., this value must be same as the composition string on the focused michael@0: // editor. This value is modified at a call of EditorDidHandleTextEvent(). michael@0: // Note that mString and mLastData are different between dispatcing michael@0: // compositionupdate and text event handled by focused editor. michael@0: const nsString& String() const { return mString; } michael@0: // Returns the clauses and/or caret range of the composition string. michael@0: // This is modified at a call of EditorWillHandleTextEvent(). michael@0: // This may return null if there is no clauses and caret. michael@0: // XXX We should return |const TextRangeArray*| here, but it causes compile michael@0: // error due to inaccessible Release() method. michael@0: TextRangeArray* GetRanges() const { return mRanges; } michael@0: // Returns true if the composition is started with synthesized event which michael@0: // came from nsDOMWindowUtils. michael@0: bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; } michael@0: michael@0: bool MatchesNativeContext(nsIWidget* aWidget) const; michael@0: michael@0: /** michael@0: * This is called when IMEStateManager stops managing the instance. michael@0: */ michael@0: void Destroy(); michael@0: michael@0: /** michael@0: * SynthesizeCommit() dispatches compositionupdate, text and compositionend michael@0: * events for emulating commit on the content. michael@0: * michael@0: * @param aDiscard true when committing with empty string. Otherwise, false. michael@0: */ michael@0: void SynthesizeCommit(bool aDiscard); michael@0: michael@0: /** michael@0: * Send a notification to IME. It depends on the IME or platform spec what michael@0: * will occur (or not occur). michael@0: */ michael@0: nsresult NotifyIME(widget::IMEMessage aMessage); michael@0: michael@0: /** michael@0: * the offset of first selected clause or start of of compositon michael@0: */ michael@0: uint32_t OffsetOfTargetClause() const { return mCompositionTargetOffset; } michael@0: michael@0: /** michael@0: * Returns true if there is non-empty composition string and it's not fixed. michael@0: * Otherwise, false. michael@0: */ michael@0: bool IsComposing() const { return mIsComposing; } michael@0: michael@0: /** michael@0: * Returns true while editor is handling an event which is modifying the michael@0: * composition string. michael@0: */ michael@0: bool IsEditorHandlingEvent() const michael@0: { michael@0: return mIsEditorHandlingEvent; michael@0: } michael@0: michael@0: /** michael@0: * StartHandlingComposition() and EndHandlingComposition() are called by michael@0: * editor when it holds a TextComposition instance and release it. michael@0: */ michael@0: void StartHandlingComposition(nsIEditor* aEditor); michael@0: void EndHandlingComposition(nsIEditor* aEditor); michael@0: michael@0: /** michael@0: * TextEventHandlingMarker class should be created at starting to handle text michael@0: * event in focused editor. This calls EditorWillHandleTextEvent() and michael@0: * EditorDidHandleTextEvent() automatically. michael@0: */ michael@0: class MOZ_STACK_CLASS TextEventHandlingMarker michael@0: { michael@0: public: michael@0: TextEventHandlingMarker(TextComposition* aComposition, michael@0: const WidgetTextEvent* aTextEvent) michael@0: : mComposition(aComposition) michael@0: { michael@0: mComposition->EditorWillHandleTextEvent(aTextEvent); michael@0: } michael@0: michael@0: ~TextEventHandlingMarker() michael@0: { michael@0: mComposition->EditorDidHandleTextEvent(); michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mComposition; michael@0: TextEventHandlingMarker(); michael@0: TextEventHandlingMarker(const TextEventHandlingMarker& aOther); michael@0: }; michael@0: michael@0: private: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~TextComposition() michael@0: { michael@0: // WARNING: mPresContext may be destroying, so, be careful if you touch it. michael@0: } michael@0: michael@0: // This class holds nsPresContext weak. This instance shouldn't block michael@0: // destroying it. When the presContext is being destroyed, it's notified to michael@0: // IMEStateManager::OnDestroyPresContext(), and then, it destroy michael@0: // this instance. michael@0: nsPresContext* mPresContext; michael@0: nsCOMPtr mNode; michael@0: michael@0: // This is the clause and caret range information which is managed by michael@0: // the focused editor. This may be null if there is no clauses or caret. michael@0: nsRefPtr mRanges; michael@0: michael@0: // mNativeContext stores a opaque pointer. This works as the "ID" for this michael@0: // composition. Don't access the instance, it may not be available. michael@0: void* mNativeContext; michael@0: michael@0: // mEditorWeak is a weak reference to the focused editor handling composition. michael@0: nsWeakPtr mEditorWeak; michael@0: michael@0: // mLastData stores the data attribute of the latest composition event (except michael@0: // the compositionstart event). michael@0: nsString mLastData; michael@0: michael@0: // mString stores the composition text which has been handled by the focused michael@0: // editor. michael@0: nsString mString; michael@0: michael@0: // Offset of the composition string from start of the editor michael@0: uint32_t mCompositionStartOffset; michael@0: // Offset of the selected clause of the composition string from start of the michael@0: // editor michael@0: uint32_t mCompositionTargetOffset; michael@0: michael@0: // See the comment for IsSynthesizedForTests(). michael@0: bool mIsSynthesizedForTests; michael@0: michael@0: // See the comment for IsComposing(). michael@0: bool mIsComposing; michael@0: michael@0: // mIsEditorHandlingEvent is true while editor is modifying the composition michael@0: // string. michael@0: bool mIsEditorHandlingEvent; michael@0: michael@0: // Hide the default constructor and copy constructor. michael@0: TextComposition() {} michael@0: TextComposition(const TextComposition& aOther); michael@0: michael@0: /** michael@0: * GetEditor() returns nsIEditor pointer of mEditorWeak. michael@0: */ michael@0: already_AddRefed GetEditor() const; michael@0: michael@0: /** michael@0: * HasEditor() returns true if mEditorWeak holds nsIEditor instance which is michael@0: * alive. Otherwise, false. michael@0: */ michael@0: bool HasEditor() const; michael@0: michael@0: /** michael@0: * EditorWillHandleTextEvent() must be called before the focused editor michael@0: * handles the text event. michael@0: */ michael@0: void EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent); michael@0: michael@0: /** michael@0: * EditorDidHandleTextEvent() must be called after the focused editor handles michael@0: * a text event. michael@0: */ michael@0: void EditorDidHandleTextEvent(); michael@0: michael@0: /** michael@0: * DispatchEvent() dispatches the aEvent to the mContent synchronously. michael@0: * The caller must ensure that it's safe to dispatch the event. michael@0: */ michael@0: void DispatchEvent(WidgetGUIEvent* aEvent, michael@0: nsEventStatus* aStatus, michael@0: EventDispatchingCallback* aCallBack); michael@0: michael@0: /** michael@0: * Calculate composition offset then notify composition update to widget michael@0: */ michael@0: void NotityUpdateComposition(WidgetGUIEvent* aEvent); michael@0: michael@0: /** michael@0: * CompositionEventDispatcher dispatches the specified composition (or text) michael@0: * event. michael@0: */ michael@0: class CompositionEventDispatcher : public nsRunnable michael@0: { michael@0: public: michael@0: CompositionEventDispatcher(nsPresContext* aPresContext, michael@0: nsINode* aEventTarget, michael@0: uint32_t aEventMessage, michael@0: const nsAString& aData); michael@0: NS_IMETHOD Run() MOZ_OVERRIDE; michael@0: michael@0: private: michael@0: nsRefPtr mPresContext; michael@0: nsCOMPtr mEventTarget; michael@0: nsCOMPtr mWidget; michael@0: uint32_t mEventMessage; michael@0: nsString mData; michael@0: michael@0: CompositionEventDispatcher() {}; michael@0: }; michael@0: michael@0: /** michael@0: * DispatchCompositionEventRunnable() dispatches a composition or text event michael@0: * to the content. Be aware, if you use this method, nsPresShellEventCB michael@0: * isn't used. That means that nsIFrame::HandleEvent() is never called. michael@0: * WARNING: The instance which is managed by IMEStateManager may be michael@0: * destroyed by this method call. michael@0: * michael@0: * @param aEventMessage Must be one of composition event or text event. michael@0: * @param aData Used for data value if aEventMessage is michael@0: * NS_COMPOSITION_UPDATE or NS_COMPOSITION_END. michael@0: * Used for theText value if aEventMessage is michael@0: * NS_TEXT_TEXT. michael@0: */ michael@0: void DispatchCompositionEventRunnable(uint32_t aEventMessage, michael@0: const nsAString& aData); michael@0: }; michael@0: michael@0: /** michael@0: * TextCompositionArray manages the instances of TextComposition class. michael@0: * Managing with array is enough because only one composition is typically michael@0: * there. Even if user switches native IME context, it's very rare that michael@0: * second or more composition is started. michael@0: * It's assumed that this is used by IMEStateManager for storing all active michael@0: * compositions in the process. If the instance is it, each TextComposition michael@0: * in the array can be destroyed by calling some methods of itself. michael@0: */ michael@0: michael@0: class TextCompositionArray MOZ_FINAL : michael@0: public nsAutoTArray, 2> michael@0: { michael@0: public: michael@0: index_type IndexOf(nsIWidget* aWidget); michael@0: index_type IndexOf(nsPresContext* aPresContext); michael@0: index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode); michael@0: michael@0: TextComposition* GetCompositionFor(nsIWidget* aWidget); michael@0: TextComposition* GetCompositionFor(nsPresContext* aPresContext, michael@0: nsINode* aNode); michael@0: TextComposition* GetCompositionInContent(nsPresContext* aPresContext, michael@0: nsIContent* aContent); michael@0: }; michael@0: michael@0: } // namespace mozilla michael@0: michael@0: #endif // #ifndef mozilla_TextComposition_h