|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 sw=2 et 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/. */ |
|
6 |
|
7 #ifndef mozilla_TextComposition_h |
|
8 #define mozilla_TextComposition_h |
|
9 |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsINode.h" |
|
12 #include "nsIWeakReference.h" |
|
13 #include "nsIWidget.h" |
|
14 #include "nsTArray.h" |
|
15 #include "nsThreadUtils.h" |
|
16 #include "nsPresContext.h" |
|
17 #include "mozilla/Attributes.h" |
|
18 #include "mozilla/EventForwards.h" |
|
19 #include "mozilla/TextRange.h" |
|
20 |
|
21 class nsIEditor; |
|
22 |
|
23 namespace mozilla { |
|
24 |
|
25 class EventDispatchingCallback; |
|
26 class IMEStateManager; |
|
27 |
|
28 /** |
|
29 * TextComposition represents a text composition. This class stores the |
|
30 * composition event target and its presContext. At dispatching the event via |
|
31 * this class, the instances use the stored event target. |
|
32 */ |
|
33 |
|
34 class TextComposition MOZ_FINAL |
|
35 { |
|
36 friend class IMEStateManager; |
|
37 |
|
38 NS_INLINE_DECL_REFCOUNTING(TextComposition) |
|
39 |
|
40 public: |
|
41 TextComposition(nsPresContext* aPresContext, |
|
42 nsINode* aNode, |
|
43 WidgetGUIEvent* aEvent); |
|
44 |
|
45 bool Destroyed() const { return !mPresContext; } |
|
46 nsPresContext* GetPresContext() const { return mPresContext; } |
|
47 nsINode* GetEventTargetNode() const { return mNode; } |
|
48 // The latest CompositionEvent.data value except compositionstart event. |
|
49 // This value is modified at dispatching compositionupdate. |
|
50 const nsString& LastData() const { return mLastData; } |
|
51 // The composition string which is already handled by the focused editor. |
|
52 // I.e., this value must be same as the composition string on the focused |
|
53 // editor. This value is modified at a call of EditorDidHandleTextEvent(). |
|
54 // Note that mString and mLastData are different between dispatcing |
|
55 // compositionupdate and text event handled by focused editor. |
|
56 const nsString& String() const { return mString; } |
|
57 // Returns the clauses and/or caret range of the composition string. |
|
58 // This is modified at a call of EditorWillHandleTextEvent(). |
|
59 // This may return null if there is no clauses and caret. |
|
60 // XXX We should return |const TextRangeArray*| here, but it causes compile |
|
61 // error due to inaccessible Release() method. |
|
62 TextRangeArray* GetRanges() const { return mRanges; } |
|
63 // Returns true if the composition is started with synthesized event which |
|
64 // came from nsDOMWindowUtils. |
|
65 bool IsSynthesizedForTests() const { return mIsSynthesizedForTests; } |
|
66 |
|
67 bool MatchesNativeContext(nsIWidget* aWidget) const; |
|
68 |
|
69 /** |
|
70 * This is called when IMEStateManager stops managing the instance. |
|
71 */ |
|
72 void Destroy(); |
|
73 |
|
74 /** |
|
75 * SynthesizeCommit() dispatches compositionupdate, text and compositionend |
|
76 * events for emulating commit on the content. |
|
77 * |
|
78 * @param aDiscard true when committing with empty string. Otherwise, false. |
|
79 */ |
|
80 void SynthesizeCommit(bool aDiscard); |
|
81 |
|
82 /** |
|
83 * Send a notification to IME. It depends on the IME or platform spec what |
|
84 * will occur (or not occur). |
|
85 */ |
|
86 nsresult NotifyIME(widget::IMEMessage aMessage); |
|
87 |
|
88 /** |
|
89 * the offset of first selected clause or start of of compositon |
|
90 */ |
|
91 uint32_t OffsetOfTargetClause() const { return mCompositionTargetOffset; } |
|
92 |
|
93 /** |
|
94 * Returns true if there is non-empty composition string and it's not fixed. |
|
95 * Otherwise, false. |
|
96 */ |
|
97 bool IsComposing() const { return mIsComposing; } |
|
98 |
|
99 /** |
|
100 * Returns true while editor is handling an event which is modifying the |
|
101 * composition string. |
|
102 */ |
|
103 bool IsEditorHandlingEvent() const |
|
104 { |
|
105 return mIsEditorHandlingEvent; |
|
106 } |
|
107 |
|
108 /** |
|
109 * StartHandlingComposition() and EndHandlingComposition() are called by |
|
110 * editor when it holds a TextComposition instance and release it. |
|
111 */ |
|
112 void StartHandlingComposition(nsIEditor* aEditor); |
|
113 void EndHandlingComposition(nsIEditor* aEditor); |
|
114 |
|
115 /** |
|
116 * TextEventHandlingMarker class should be created at starting to handle text |
|
117 * event in focused editor. This calls EditorWillHandleTextEvent() and |
|
118 * EditorDidHandleTextEvent() automatically. |
|
119 */ |
|
120 class MOZ_STACK_CLASS TextEventHandlingMarker |
|
121 { |
|
122 public: |
|
123 TextEventHandlingMarker(TextComposition* aComposition, |
|
124 const WidgetTextEvent* aTextEvent) |
|
125 : mComposition(aComposition) |
|
126 { |
|
127 mComposition->EditorWillHandleTextEvent(aTextEvent); |
|
128 } |
|
129 |
|
130 ~TextEventHandlingMarker() |
|
131 { |
|
132 mComposition->EditorDidHandleTextEvent(); |
|
133 } |
|
134 |
|
135 private: |
|
136 nsRefPtr<TextComposition> mComposition; |
|
137 TextEventHandlingMarker(); |
|
138 TextEventHandlingMarker(const TextEventHandlingMarker& aOther); |
|
139 }; |
|
140 |
|
141 private: |
|
142 // Private destructor, to discourage deletion outside of Release(): |
|
143 ~TextComposition() |
|
144 { |
|
145 // WARNING: mPresContext may be destroying, so, be careful if you touch it. |
|
146 } |
|
147 |
|
148 // This class holds nsPresContext weak. This instance shouldn't block |
|
149 // destroying it. When the presContext is being destroyed, it's notified to |
|
150 // IMEStateManager::OnDestroyPresContext(), and then, it destroy |
|
151 // this instance. |
|
152 nsPresContext* mPresContext; |
|
153 nsCOMPtr<nsINode> mNode; |
|
154 |
|
155 // This is the clause and caret range information which is managed by |
|
156 // the focused editor. This may be null if there is no clauses or caret. |
|
157 nsRefPtr<TextRangeArray> mRanges; |
|
158 |
|
159 // mNativeContext stores a opaque pointer. This works as the "ID" for this |
|
160 // composition. Don't access the instance, it may not be available. |
|
161 void* mNativeContext; |
|
162 |
|
163 // mEditorWeak is a weak reference to the focused editor handling composition. |
|
164 nsWeakPtr mEditorWeak; |
|
165 |
|
166 // mLastData stores the data attribute of the latest composition event (except |
|
167 // the compositionstart event). |
|
168 nsString mLastData; |
|
169 |
|
170 // mString stores the composition text which has been handled by the focused |
|
171 // editor. |
|
172 nsString mString; |
|
173 |
|
174 // Offset of the composition string from start of the editor |
|
175 uint32_t mCompositionStartOffset; |
|
176 // Offset of the selected clause of the composition string from start of the |
|
177 // editor |
|
178 uint32_t mCompositionTargetOffset; |
|
179 |
|
180 // See the comment for IsSynthesizedForTests(). |
|
181 bool mIsSynthesizedForTests; |
|
182 |
|
183 // See the comment for IsComposing(). |
|
184 bool mIsComposing; |
|
185 |
|
186 // mIsEditorHandlingEvent is true while editor is modifying the composition |
|
187 // string. |
|
188 bool mIsEditorHandlingEvent; |
|
189 |
|
190 // Hide the default constructor and copy constructor. |
|
191 TextComposition() {} |
|
192 TextComposition(const TextComposition& aOther); |
|
193 |
|
194 /** |
|
195 * GetEditor() returns nsIEditor pointer of mEditorWeak. |
|
196 */ |
|
197 already_AddRefed<nsIEditor> GetEditor() const; |
|
198 |
|
199 /** |
|
200 * HasEditor() returns true if mEditorWeak holds nsIEditor instance which is |
|
201 * alive. Otherwise, false. |
|
202 */ |
|
203 bool HasEditor() const; |
|
204 |
|
205 /** |
|
206 * EditorWillHandleTextEvent() must be called before the focused editor |
|
207 * handles the text event. |
|
208 */ |
|
209 void EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent); |
|
210 |
|
211 /** |
|
212 * EditorDidHandleTextEvent() must be called after the focused editor handles |
|
213 * a text event. |
|
214 */ |
|
215 void EditorDidHandleTextEvent(); |
|
216 |
|
217 /** |
|
218 * DispatchEvent() dispatches the aEvent to the mContent synchronously. |
|
219 * The caller must ensure that it's safe to dispatch the event. |
|
220 */ |
|
221 void DispatchEvent(WidgetGUIEvent* aEvent, |
|
222 nsEventStatus* aStatus, |
|
223 EventDispatchingCallback* aCallBack); |
|
224 |
|
225 /** |
|
226 * Calculate composition offset then notify composition update to widget |
|
227 */ |
|
228 void NotityUpdateComposition(WidgetGUIEvent* aEvent); |
|
229 |
|
230 /** |
|
231 * CompositionEventDispatcher dispatches the specified composition (or text) |
|
232 * event. |
|
233 */ |
|
234 class CompositionEventDispatcher : public nsRunnable |
|
235 { |
|
236 public: |
|
237 CompositionEventDispatcher(nsPresContext* aPresContext, |
|
238 nsINode* aEventTarget, |
|
239 uint32_t aEventMessage, |
|
240 const nsAString& aData); |
|
241 NS_IMETHOD Run() MOZ_OVERRIDE; |
|
242 |
|
243 private: |
|
244 nsRefPtr<nsPresContext> mPresContext; |
|
245 nsCOMPtr<nsINode> mEventTarget; |
|
246 nsCOMPtr<nsIWidget> mWidget; |
|
247 uint32_t mEventMessage; |
|
248 nsString mData; |
|
249 |
|
250 CompositionEventDispatcher() {}; |
|
251 }; |
|
252 |
|
253 /** |
|
254 * DispatchCompositionEventRunnable() dispatches a composition or text event |
|
255 * to the content. Be aware, if you use this method, nsPresShellEventCB |
|
256 * isn't used. That means that nsIFrame::HandleEvent() is never called. |
|
257 * WARNING: The instance which is managed by IMEStateManager may be |
|
258 * destroyed by this method call. |
|
259 * |
|
260 * @param aEventMessage Must be one of composition event or text event. |
|
261 * @param aData Used for data value if aEventMessage is |
|
262 * NS_COMPOSITION_UPDATE or NS_COMPOSITION_END. |
|
263 * Used for theText value if aEventMessage is |
|
264 * NS_TEXT_TEXT. |
|
265 */ |
|
266 void DispatchCompositionEventRunnable(uint32_t aEventMessage, |
|
267 const nsAString& aData); |
|
268 }; |
|
269 |
|
270 /** |
|
271 * TextCompositionArray manages the instances of TextComposition class. |
|
272 * Managing with array is enough because only one composition is typically |
|
273 * there. Even if user switches native IME context, it's very rare that |
|
274 * second or more composition is started. |
|
275 * It's assumed that this is used by IMEStateManager for storing all active |
|
276 * compositions in the process. If the instance is it, each TextComposition |
|
277 * in the array can be destroyed by calling some methods of itself. |
|
278 */ |
|
279 |
|
280 class TextCompositionArray MOZ_FINAL : |
|
281 public nsAutoTArray<nsRefPtr<TextComposition>, 2> |
|
282 { |
|
283 public: |
|
284 index_type IndexOf(nsIWidget* aWidget); |
|
285 index_type IndexOf(nsPresContext* aPresContext); |
|
286 index_type IndexOf(nsPresContext* aPresContext, nsINode* aNode); |
|
287 |
|
288 TextComposition* GetCompositionFor(nsIWidget* aWidget); |
|
289 TextComposition* GetCompositionFor(nsPresContext* aPresContext, |
|
290 nsINode* aNode); |
|
291 TextComposition* GetCompositionInContent(nsPresContext* aPresContext, |
|
292 nsIContent* aContent); |
|
293 }; |
|
294 |
|
295 } // namespace mozilla |
|
296 |
|
297 #endif // #ifndef mozilla_TextComposition_h |