dom/events/TextComposition.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:1c54d48b425a
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 #include "ContentEventHandler.h"
8 #include "nsContentUtils.h"
9 #include "nsIContent.h"
10 #include "nsIEditor.h"
11 #include "nsIPresShell.h"
12 #include "nsPresContext.h"
13 #include "mozilla/EventDispatcher.h"
14 #include "mozilla/IMEStateManager.h"
15 #include "mozilla/MiscEvents.h"
16 #include "mozilla/TextComposition.h"
17 #include "mozilla/TextEvents.h"
18
19 using namespace mozilla::widget;
20
21 namespace mozilla {
22
23 /******************************************************************************
24 * TextComposition
25 ******************************************************************************/
26
27 TextComposition::TextComposition(nsPresContext* aPresContext,
28 nsINode* aNode,
29 WidgetGUIEvent* aEvent)
30 : mPresContext(aPresContext)
31 , mNode(aNode)
32 , mNativeContext(aEvent->widget->GetInputContext().mNativeIMEContext)
33 , mCompositionStartOffset(0)
34 , mCompositionTargetOffset(0)
35 , mIsSynthesizedForTests(aEvent->mFlags.mIsSynthesizedForTests)
36 , mIsComposing(false)
37 , mIsEditorHandlingEvent(false)
38 {
39 }
40
41 void
42 TextComposition::Destroy()
43 {
44 mPresContext = nullptr;
45 mNode = nullptr;
46 // TODO: If the editor is still alive and this is held by it, we should tell
47 // this being destroyed for cleaning up the stuff.
48 }
49
50 bool
51 TextComposition::MatchesNativeContext(nsIWidget* aWidget) const
52 {
53 return mNativeContext == aWidget->GetInputContext().mNativeIMEContext;
54 }
55
56 void
57 TextComposition::DispatchEvent(WidgetGUIEvent* aEvent,
58 nsEventStatus* aStatus,
59 EventDispatchingCallback* aCallBack)
60 {
61 if (aEvent->message == NS_COMPOSITION_UPDATE) {
62 mLastData = aEvent->AsCompositionEvent()->data;
63 }
64
65 EventDispatcher::Dispatch(mNode, mPresContext,
66 aEvent, nullptr, aStatus, aCallBack);
67
68 if (!mPresContext) {
69 return;
70 }
71
72 // Emulate editor behavior of text event handler if no editor handles
73 // composition/text events.
74 if (aEvent->message == NS_TEXT_TEXT && !HasEditor()) {
75 EditorWillHandleTextEvent(aEvent->AsTextEvent());
76 EditorDidHandleTextEvent();
77 }
78
79 #ifdef DEBUG
80 else if (aEvent->message == NS_COMPOSITION_END) {
81 MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?");
82 MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
83 }
84 #endif // #ifdef DEBUG
85
86 // Notify composition update to widget if possible
87 NotityUpdateComposition(aEvent);
88 }
89
90 void
91 TextComposition::NotityUpdateComposition(WidgetGUIEvent* aEvent)
92 {
93 nsEventStatus status;
94
95 // When compositon start, notify the rect of first offset character.
96 // When not compositon start, notify the rect of selected composition
97 // string if text event.
98 if (aEvent->message == NS_COMPOSITION_START) {
99 nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
100 // Update composition start offset
101 WidgetQueryContentEvent selectedTextEvent(true,
102 NS_QUERY_SELECTED_TEXT,
103 widget);
104 widget->DispatchEvent(&selectedTextEvent, status);
105 if (selectedTextEvent.mSucceeded) {
106 mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
107 } else {
108 // Unknown offset
109 NS_WARNING("Cannot get start offset of IME composition");
110 mCompositionStartOffset = 0;
111 }
112 mCompositionTargetOffset = mCompositionStartOffset;
113 } else if (aEvent->eventStructType != NS_TEXT_EVENT) {
114 return;
115 } else {
116 mCompositionTargetOffset =
117 mCompositionStartOffset + aEvent->AsTextEvent()->TargetClauseOffset();
118 }
119
120 NotifyIME(NOTIFY_IME_OF_COMPOSITION_UPDATE);
121 }
122
123 void
124 TextComposition::DispatchCompositionEventRunnable(uint32_t aEventMessage,
125 const nsAString& aData)
126 {
127 nsContentUtils::AddScriptRunner(
128 new CompositionEventDispatcher(mPresContext, mNode,
129 aEventMessage, aData));
130 }
131
132 void
133 TextComposition::SynthesizeCommit(bool aDiscard)
134 {
135 nsRefPtr<TextComposition> kungFuDeathGrip(this);
136 nsAutoString data(aDiscard ? EmptyString() : mLastData);
137 if (mLastData != data) {
138 DispatchCompositionEventRunnable(NS_COMPOSITION_UPDATE, data);
139 DispatchCompositionEventRunnable(NS_TEXT_TEXT, data);
140 }
141 DispatchCompositionEventRunnable(NS_COMPOSITION_END, data);
142 }
143
144 nsresult
145 TextComposition::NotifyIME(IMEMessage aMessage)
146 {
147 NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
148 return IMEStateManager::NotifyIME(aMessage, mPresContext);
149 }
150
151 void
152 TextComposition::EditorWillHandleTextEvent(const WidgetTextEvent* aTextEvent)
153 {
154 mIsComposing = aTextEvent->IsComposing();
155 mRanges = aTextEvent->mRanges;
156 mIsEditorHandlingEvent = true;
157
158 MOZ_ASSERT(mLastData == aTextEvent->theText,
159 "The text of a text event must be same as previous data attribute value "
160 "of the latest compositionupdate event");
161 }
162
163 void
164 TextComposition::EditorDidHandleTextEvent()
165 {
166 mString = mLastData;
167 mIsEditorHandlingEvent = false;
168 }
169
170 void
171 TextComposition::StartHandlingComposition(nsIEditor* aEditor)
172 {
173 MOZ_ASSERT(!HasEditor(), "There is a handling editor already");
174 mEditorWeak = do_GetWeakReference(aEditor);
175 }
176
177 void
178 TextComposition::EndHandlingComposition(nsIEditor* aEditor)
179 {
180 #ifdef DEBUG
181 nsCOMPtr<nsIEditor> editor = GetEditor();
182 MOZ_ASSERT(editor == aEditor, "Another editor handled the composition?");
183 #endif // #ifdef DEBUG
184 mEditorWeak = nullptr;
185 }
186
187 already_AddRefed<nsIEditor>
188 TextComposition::GetEditor() const
189 {
190 nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditorWeak);
191 return editor.forget();
192 }
193
194 bool
195 TextComposition::HasEditor() const
196 {
197 nsCOMPtr<nsIEditor> editor = GetEditor();
198 return !!editor;
199 }
200
201 /******************************************************************************
202 * TextComposition::CompositionEventDispatcher
203 ******************************************************************************/
204
205 TextComposition::CompositionEventDispatcher::CompositionEventDispatcher(
206 nsPresContext* aPresContext,
207 nsINode* aEventTarget,
208 uint32_t aEventMessage,
209 const nsAString& aData) :
210 mPresContext(aPresContext), mEventTarget(aEventTarget),
211 mEventMessage(aEventMessage), mData(aData)
212 {
213 mWidget = mPresContext->GetRootWidget();
214 }
215
216 NS_IMETHODIMP
217 TextComposition::CompositionEventDispatcher::Run()
218 {
219 if (!mPresContext->GetPresShell() ||
220 mPresContext->GetPresShell()->IsDestroying()) {
221 return NS_OK; // cannot dispatch any events anymore
222 }
223
224 nsEventStatus status = nsEventStatus_eIgnore;
225 switch (mEventMessage) {
226 case NS_COMPOSITION_START: {
227 WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
228 WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT,
229 mWidget);
230 ContentEventHandler handler(mPresContext);
231 handler.OnQuerySelectedText(&selectedText);
232 NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
233 compStart.data = selectedText.mReply.mString;
234 IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
235 &compStart, &status, nullptr);
236 break;
237 }
238 case NS_COMPOSITION_UPDATE:
239 case NS_COMPOSITION_END: {
240 WidgetCompositionEvent compEvent(true, mEventMessage, mWidget);
241 compEvent.data = mData;
242 IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
243 &compEvent, &status, nullptr);
244 break;
245 }
246 case NS_TEXT_TEXT: {
247 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
248 textEvent.theText = mData;
249 IMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext,
250 &textEvent, &status, nullptr);
251 break;
252 }
253 default:
254 MOZ_CRASH("Unsupported event");
255 }
256 return NS_OK;
257 }
258
259 /******************************************************************************
260 * TextCompositionArray
261 ******************************************************************************/
262
263 TextCompositionArray::index_type
264 TextCompositionArray::IndexOf(nsIWidget* aWidget)
265 {
266 for (index_type i = Length(); i > 0; --i) {
267 if (ElementAt(i - 1)->MatchesNativeContext(aWidget)) {
268 return i - 1;
269 }
270 }
271 return NoIndex;
272 }
273
274 TextCompositionArray::index_type
275 TextCompositionArray::IndexOf(nsPresContext* aPresContext)
276 {
277 for (index_type i = Length(); i > 0; --i) {
278 if (ElementAt(i - 1)->GetPresContext() == aPresContext) {
279 return i - 1;
280 }
281 }
282 return NoIndex;
283 }
284
285 TextCompositionArray::index_type
286 TextCompositionArray::IndexOf(nsPresContext* aPresContext,
287 nsINode* aNode)
288 {
289 index_type index = IndexOf(aPresContext);
290 if (index == NoIndex) {
291 return NoIndex;
292 }
293 nsINode* node = ElementAt(index)->GetEventTargetNode();
294 return node == aNode ? index : NoIndex;
295 }
296
297 TextComposition*
298 TextCompositionArray::GetCompositionFor(nsIWidget* aWidget)
299 {
300 index_type i = IndexOf(aWidget);
301 return i != NoIndex ? ElementAt(i) : nullptr;
302 }
303
304 TextComposition*
305 TextCompositionArray::GetCompositionFor(nsPresContext* aPresContext,
306 nsINode* aNode)
307 {
308 index_type i = IndexOf(aPresContext, aNode);
309 return i != NoIndex ? ElementAt(i) : nullptr;
310 }
311
312 TextComposition*
313 TextCompositionArray::GetCompositionInContent(nsPresContext* aPresContext,
314 nsIContent* aContent)
315 {
316 // There should be only one composition per content object.
317 for (index_type i = Length(); i > 0; --i) {
318 nsINode* node = ElementAt(i - 1)->GetEventTargetNode();
319 if (node && nsContentUtils::ContentIsDescendantOf(node, aContent)) {
320 return ElementAt(i - 1);
321 }
322 }
323 return nullptr;
324 }
325
326 } // namespace mozilla

mercurial