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/. */
7 #include "mozilla/IMEStateManager.h"
9 #include "mozilla/Attributes.h"
10 #include "mozilla/EventStates.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/Services.h"
13 #include "mozilla/TextComposition.h"
14 #include "mozilla/TextEvents.h"
15 #include "mozilla/dom/HTMLFormElement.h"
17 #include "HTMLInputElement.h"
18 #include "IMEContentObserver.h"
21 #include "nsContentUtils.h"
22 #include "nsIContent.h"
23 #include "nsIDocument.h"
24 #include "nsIDOMMouseEvent.h"
26 #include "nsIFormControl.h"
28 #include "nsIObserverService.h"
29 #include "nsIPresShell.h"
30 #include "nsISelection.h"
31 #include "nsISupports.h"
32 #include "nsPresContext.h"
37 using namespace widget
;
39 nsIContent
* IMEStateManager::sContent
= nullptr;
40 nsPresContext
* IMEStateManager::sPresContext
= nullptr;
41 bool IMEStateManager::sInstalledMenuKeyboardListener
= false;
42 bool IMEStateManager::sIsTestingIME
= false;
44 // sActiveIMEContentObserver points to the currently active IMEContentObserver.
45 // sActiveIMEContentObserver is null if there is no focused editor.
46 IMEContentObserver
* IMEStateManager::sActiveIMEContentObserver
= nullptr;
47 TextCompositionArray
* IMEStateManager::sTextCompositions
= nullptr;
50 IMEStateManager::Shutdown()
52 MOZ_ASSERT(!sTextCompositions
|| !sTextCompositions
->Length());
53 delete sTextCompositions
;
54 sTextCompositions
= nullptr;
58 IMEStateManager::OnDestroyPresContext(nsPresContext
* aPresContext
)
60 NS_ENSURE_ARG_POINTER(aPresContext
);
62 // First, if there is a composition in the aPresContext, clean up it.
63 if (sTextCompositions
) {
64 TextCompositionArray::index_type i
=
65 sTextCompositions
->IndexOf(aPresContext
);
66 if (i
!= TextCompositionArray::NoIndex
) {
67 // there should be only one composition per presContext object.
68 sTextCompositions
->ElementAt(i
)->Destroy();
69 sTextCompositions
->RemoveElementAt(i
);
70 MOZ_ASSERT(sTextCompositions
->IndexOf(aPresContext
) ==
71 TextCompositionArray::NoIndex
);
75 if (aPresContext
!= sPresContext
) {
79 DestroyTextStateManager();
81 nsCOMPtr
<nsIWidget
> widget
= sPresContext
->GetRootWidget();
83 IMEState newState
= GetNewIMEState(sPresContext
, nullptr);
84 InputContextAction
action(InputContextAction::CAUSE_UNKNOWN
,
85 InputContextAction::LOST_FOCUS
);
86 SetIMEState(newState
, nullptr, widget
, action
);
88 NS_IF_RELEASE(sContent
);
89 sPresContext
= nullptr;
94 IMEStateManager::OnRemoveContent(nsPresContext
* aPresContext
,
97 NS_ENSURE_ARG_POINTER(aPresContext
);
99 // First, if there is a composition in the aContent, clean up it.
100 if (sTextCompositions
) {
101 nsRefPtr
<TextComposition
> compositionInContent
=
102 sTextCompositions
->GetCompositionInContent(aPresContext
, aContent
);
104 if (compositionInContent
) {
105 // Try resetting the native IME state. Be aware, typically, this method
106 // is called during the content being removed. Then, the native
107 // composition events which are caused by following APIs are ignored due
108 // to unsafe to run script (in PresShell::HandleEvent()).
109 nsCOMPtr
<nsIWidget
> widget
= aPresContext
->GetRootWidget();
112 compositionInContent
->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION
);
114 compositionInContent
->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION
);
116 // By calling the APIs, the composition may have been finished normally.
117 compositionInContent
=
118 sTextCompositions
->GetCompositionFor(
119 compositionInContent
->GetPresContext(),
120 compositionInContent
->GetEventTargetNode());
124 // If the compositionInContent is still available, we should finish the
125 // composition just on the content forcibly.
126 if (compositionInContent
) {
127 compositionInContent
->SynthesizeCommit(true);
131 if (!sPresContext
|| !sContent
||
132 !nsContentUtils::ContentIsDescendantOf(sContent
, aContent
)) {
136 DestroyTextStateManager();
138 // Current IME transaction should commit
139 nsCOMPtr
<nsIWidget
> widget
= sPresContext
->GetRootWidget();
141 IMEState newState
= GetNewIMEState(sPresContext
, nullptr);
142 InputContextAction
action(InputContextAction::CAUSE_UNKNOWN
,
143 InputContextAction::LOST_FOCUS
);
144 SetIMEState(newState
, nullptr, widget
, action
);
147 NS_IF_RELEASE(sContent
);
148 sPresContext
= nullptr;
154 IMEStateManager::OnChangeFocus(nsPresContext
* aPresContext
,
155 nsIContent
* aContent
,
156 InputContextAction::Cause aCause
)
158 InputContextAction
action(aCause
);
159 return OnChangeFocusInternal(aPresContext
, aContent
, action
);
163 IMEStateManager::OnChangeFocusInternal(nsPresContext
* aPresContext
,
164 nsIContent
* aContent
,
165 InputContextAction aAction
)
167 bool focusActuallyChanging
=
168 (sContent
!= aContent
|| sPresContext
!= aPresContext
);
170 nsCOMPtr
<nsIWidget
> oldWidget
=
171 sPresContext
? sPresContext
->GetRootWidget() : nullptr;
172 if (oldWidget
&& focusActuallyChanging
) {
173 // If we're deactivating, we shouldn't commit composition forcibly because
174 // the user may want to continue the composition.
176 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION
, oldWidget
);
180 if (sActiveIMEContentObserver
&&
181 (aPresContext
|| !sActiveIMEContentObserver
->KeepAliveDuringDeactive()) &&
182 !sActiveIMEContentObserver
->IsManaging(aPresContext
, aContent
)) {
183 DestroyTextStateManager();
190 nsCOMPtr
<nsIWidget
> widget
=
191 (sPresContext
== aPresContext
) ? oldWidget
.get() :
192 aPresContext
->GetRootWidget();
197 IMEState newState
= GetNewIMEState(aPresContext
, aContent
);
198 if (!focusActuallyChanging
) {
199 // actual focus isn't changing, but if IME enabled state is changing,
201 InputContext context
= widget
->GetInputContext();
202 if (context
.mIMEState
.mEnabled
== newState
.mEnabled
) {
203 // the enabled state isn't changing.
206 aAction
.mFocusChange
= InputContextAction::FOCUS_NOT_CHANGED
;
208 // Even if focus isn't changing actually, we should commit current
209 // composition here since the IME state is changing.
210 if (sPresContext
&& oldWidget
&& !focusActuallyChanging
) {
211 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION
, oldWidget
);
213 } else if (aAction
.mFocusChange
== InputContextAction::FOCUS_NOT_CHANGED
) {
214 // If aContent isn't null or aContent is null but editable, somebody gets
216 bool gotFocus
= aContent
|| (newState
.mEnabled
== IMEState::ENABLED
);
217 aAction
.mFocusChange
=
218 gotFocus
? InputContextAction::GOT_FOCUS
: InputContextAction::LOST_FOCUS
;
221 // Update IME state for new focus widget
222 SetIMEState(newState
, aContent
, widget
, aAction
);
224 sPresContext
= aPresContext
;
225 if (sContent
!= aContent
) {
226 NS_IF_RELEASE(sContent
);
227 NS_IF_ADDREF(sContent
= aContent
);
230 // Don't call CreateIMEContentObserver() here, it should be called from
231 // focus event handler of editor.
237 IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling
)
239 sInstalledMenuKeyboardListener
= aInstalling
;
241 InputContextAction
action(InputContextAction::CAUSE_UNKNOWN
,
242 aInstalling
? InputContextAction::MENU_GOT_PSEUDO_FOCUS
:
243 InputContextAction::MENU_LOST_PSEUDO_FOCUS
);
244 OnChangeFocusInternal(sPresContext
, sContent
, action
);
248 IMEStateManager::OnClickInEditor(nsPresContext
* aPresContext
,
249 nsIContent
* aContent
,
250 nsIDOMMouseEvent
* aMouseEvent
)
252 if (sPresContext
!= aPresContext
|| sContent
!= aContent
) {
256 nsCOMPtr
<nsIWidget
> widget
= aPresContext
->GetRootWidget();
257 NS_ENSURE_TRUE_VOID(widget
);
260 nsresult rv
= aMouseEvent
->GetIsTrusted(&isTrusted
);
261 NS_ENSURE_SUCCESS_VOID(rv
);
263 return; // ignore untrusted event.
267 rv
= aMouseEvent
->GetButton(&button
);
268 NS_ENSURE_SUCCESS_VOID(rv
);
270 return; // not a left click event.
274 rv
= aMouseEvent
->GetDetail(&clickCount
);
275 NS_ENSURE_SUCCESS_VOID(rv
);
276 if (clickCount
!= 1) {
277 return; // should notify only first click event.
280 InputContextAction
action(InputContextAction::CAUSE_MOUSE
,
281 InputContextAction::FOCUS_NOT_CHANGED
);
282 IMEState newState
= GetNewIMEState(aPresContext
, aContent
);
283 SetIMEState(newState
, aContent
, widget
, action
);
287 IMEStateManager::OnFocusInEditor(nsPresContext
* aPresContext
,
288 nsIContent
* aContent
)
290 if (sPresContext
!= aPresContext
|| sContent
!= aContent
) {
294 // If the IMEContentObserver instance isn't managing the editor actually,
295 // we need to recreate the instance.
296 if (sActiveIMEContentObserver
) {
297 if (sActiveIMEContentObserver
->IsManaging(aPresContext
, aContent
)) {
300 DestroyTextStateManager();
303 CreateIMEContentObserver();
307 IMEStateManager::UpdateIMEState(const IMEState
& aNewIMEState
,
308 nsIContent
* aContent
)
311 NS_WARNING("ISM doesn't know which editor has focus");
314 nsCOMPtr
<nsIWidget
> widget
= sPresContext
->GetRootWidget();
316 NS_WARNING("focused widget is not found");
320 // If the IMEContentObserver instance isn't managing the editor's current
321 // editable root content, the editor frame might be reframed. We should
322 // recreate the instance at that time.
323 bool createTextStateManager
=
324 (!sActiveIMEContentObserver
||
325 !sActiveIMEContentObserver
->IsManaging(sPresContext
, aContent
));
327 bool updateIMEState
=
328 (widget
->GetInputContext().mIMEState
.mEnabled
!= aNewIMEState
.mEnabled
);
330 if (updateIMEState
) {
331 // commit current composition before modifying IME state.
332 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION
, widget
);
335 if (createTextStateManager
) {
336 DestroyTextStateManager();
339 if (updateIMEState
) {
340 InputContextAction
action(InputContextAction::CAUSE_UNKNOWN
,
341 InputContextAction::FOCUS_NOT_CHANGED
);
342 SetIMEState(aNewIMEState
, aContent
, widget
, action
);
345 if (createTextStateManager
) {
346 CreateIMEContentObserver();
351 IMEStateManager::GetNewIMEState(nsPresContext
* aPresContext
,
352 nsIContent
* aContent
)
354 // On Printing or Print Preview, we don't need IME.
355 if (aPresContext
->Type() == nsPresContext::eContext_PrintPreview
||
356 aPresContext
->Type() == nsPresContext::eContext_Print
) {
357 return IMEState(IMEState::DISABLED
);
360 if (sInstalledMenuKeyboardListener
) {
361 return IMEState(IMEState::DISABLED
);
365 // Even if there are no focused content, the focused document might be
366 // editable, such case is design mode.
367 nsIDocument
* doc
= aPresContext
->Document();
368 if (doc
&& doc
->HasFlag(NODE_IS_EDITABLE
)) {
369 return IMEState(IMEState::ENABLED
);
371 return IMEState(IMEState::DISABLED
);
374 return aContent
->GetDesiredIMEState();
377 // Helper class, used for IME enabled state change notification
378 class IMEEnabledStateChangedEvent
: public nsRunnable
{
380 IMEEnabledStateChangedEvent(uint32_t aState
)
387 nsCOMPtr
<nsIObserverService
> observerService
=
388 services::GetObserverService();
389 if (observerService
) {
391 state
.AppendInt(mState
);
392 observerService
->NotifyObservers(nullptr, "ime-enabled-state-changed",
403 IMEStateManager::SetIMEState(const IMEState
& aState
,
404 nsIContent
* aContent
,
406 InputContextAction aAction
)
408 NS_ENSURE_TRUE_VOID(aWidget
);
410 InputContext oldContext
= aWidget
->GetInputContext();
412 InputContext context
;
413 context
.mIMEState
= aState
;
415 if (aContent
&& aContent
->GetNameSpaceID() == kNameSpaceID_XHTML
&&
416 (aContent
->Tag() == nsGkAtoms::input
||
417 aContent
->Tag() == nsGkAtoms::textarea
)) {
418 if (aContent
->Tag() != nsGkAtoms::textarea
) {
419 // <input type=number> has an anonymous <input type=text> descendant
420 // that gets focus whenever anyone tries to focus the number control. We
421 // need to check if aContent is one of those anonymous text controls and,
422 // if so, use the number control instead:
423 nsIContent
* content
= aContent
;
424 HTMLInputElement
* inputElement
=
425 HTMLInputElement::FromContentOrNull(aContent
);
427 HTMLInputElement
* ownerNumberControl
=
428 inputElement
->GetOwnerNumberControl();
429 if (ownerNumberControl
) {
430 content
= ownerNumberControl
; // an <input type=number>
433 content
->GetAttr(kNameSpaceID_None
, nsGkAtoms::type
,
434 context
.mHTMLInputType
);
436 context
.mHTMLInputType
.Assign(nsGkAtoms::textarea
->GetUTF16String());
439 if (Preferences::GetBool("dom.forms.inputmode", false)) {
440 aContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::inputmode
,
441 context
.mHTMLInputInputmode
);
444 aContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::moz_action_hint
,
445 context
.mActionHint
);
447 // if we don't have an action hint and return won't submit the form use "next"
448 if (context
.mActionHint
.IsEmpty() && aContent
->Tag() == nsGkAtoms::input
) {
449 bool willSubmit
= false;
450 nsCOMPtr
<nsIFormControl
> control(do_QueryInterface(aContent
));
451 mozilla::dom::Element
* formElement
= control
->GetFormElement();
452 nsCOMPtr
<nsIForm
> form
;
454 // is this a form and does it have a default submit element?
455 if ((form
= do_QueryInterface(formElement
)) &&
456 form
->GetDefaultSubmitElement()) {
458 // is this an html form and does it only have a single text input element?
459 } else if (formElement
&& formElement
->Tag() == nsGkAtoms::form
&&
460 formElement
->IsHTML() &&
461 !static_cast<dom::HTMLFormElement
*>(formElement
)->
462 ImplicitSubmissionIsDisabled()) {
466 context
.mActionHint
.Assign(
467 willSubmit
? (control
->GetType() == NS_FORM_INPUT_SEARCH
?
468 NS_LITERAL_STRING("search") : NS_LITERAL_STRING("go")) :
470 NS_LITERAL_STRING("next") : EmptyString()));
474 // XXX I think that we should use nsContentUtils::IsCallerChrome() instead
475 // of the process type.
476 if (aAction
.mCause
== InputContextAction::CAUSE_UNKNOWN
&&
477 XRE_GetProcessType() != GeckoProcessType_Content
) {
478 aAction
.mCause
= InputContextAction::CAUSE_UNKNOWN_CHROME
;
481 aWidget
->SetInputContext(context
, aAction
);
482 if (oldContext
.mIMEState
.mEnabled
== context
.mIMEState
.mEnabled
) {
486 nsContentUtils::AddScriptRunner(
487 new IMEEnabledStateChangedEvent(context
.mIMEState
.mEnabled
));
491 IMEStateManager::EnsureTextCompositionArray()
493 if (sTextCompositions
) {
496 sTextCompositions
= new TextCompositionArray();
500 IMEStateManager::DispatchCompositionEvent(nsINode
* aEventTargetNode
,
501 nsPresContext
* aPresContext
,
503 nsEventStatus
* aStatus
,
504 EventDispatchingCallback
* aCallBack
)
506 MOZ_ASSERT(aEvent
->eventStructType
== NS_COMPOSITION_EVENT
||
507 aEvent
->eventStructType
== NS_TEXT_EVENT
);
508 if (!aEvent
->mFlags
.mIsTrusted
|| aEvent
->mFlags
.mPropagationStopped
) {
512 EnsureTextCompositionArray();
514 WidgetGUIEvent
* GUIEvent
= aEvent
->AsGUIEvent();
516 nsRefPtr
<TextComposition
> composition
=
517 sTextCompositions
->GetCompositionFor(GUIEvent
->widget
);
519 MOZ_ASSERT(GUIEvent
->message
== NS_COMPOSITION_START
);
520 composition
= new TextComposition(aPresContext
, aEventTargetNode
, GUIEvent
);
521 sTextCompositions
->AppendElement(composition
);
525 MOZ_ASSERT(GUIEvent
->message
!= NS_COMPOSITION_START
);
527 #endif // #ifdef DEBUG
529 // Dispatch the event on composing target.
530 composition
->DispatchEvent(GUIEvent
, aStatus
, aCallBack
);
532 // WARNING: the |composition| might have been destroyed already.
534 // Remove the ended composition from the array.
535 if (aEvent
->message
== NS_COMPOSITION_END
) {
536 TextCompositionArray::index_type i
=
537 sTextCompositions
->IndexOf(GUIEvent
->widget
);
538 if (i
!= TextCompositionArray::NoIndex
) {
539 sTextCompositions
->ElementAt(i
)->Destroy();
540 sTextCompositions
->RemoveElementAt(i
);
547 IMEStateManager::NotifyIME(IMEMessage aMessage
,
550 NS_ENSURE_TRUE(aWidget
, NS_ERROR_INVALID_ARG
);
552 nsRefPtr
<TextComposition
> composition
;
553 if (sTextCompositions
) {
554 composition
= sTextCompositions
->GetCompositionFor(aWidget
);
556 if (!composition
|| !composition
->IsSynthesizedForTests()) {
558 case NOTIFY_IME_OF_CURSOR_POS_CHANGED
:
559 return aWidget
->NotifyIME(IMENotification(aMessage
));
560 case REQUEST_TO_COMMIT_COMPOSITION
:
561 case REQUEST_TO_CANCEL_COMPOSITION
:
562 case NOTIFY_IME_OF_COMPOSITION_UPDATE
:
564 aWidget
->NotifyIME(IMENotification(aMessage
)) : NS_OK
;
566 MOZ_CRASH("Unsupported notification");
569 "Failed to handle the notification for non-synthesized composition");
572 // If the composition is synthesized events for automated tests, we should
573 // dispatch composition events for emulating the native composition behavior.
574 // NOTE: The dispatched events are discarded if it's not safe to run script.
576 case REQUEST_TO_COMMIT_COMPOSITION
: {
577 nsCOMPtr
<nsIWidget
> widget(aWidget
);
578 nsEventStatus status
= nsEventStatus_eIgnore
;
579 if (!composition
->LastData().IsEmpty()) {
580 WidgetTextEvent
textEvent(true, NS_TEXT_TEXT
, widget
);
581 textEvent
.theText
= composition
->LastData();
582 textEvent
.mFlags
.mIsSynthesizedForTests
= true;
583 widget
->DispatchEvent(&textEvent
, status
);
584 if (widget
->Destroyed()) {
589 status
= nsEventStatus_eIgnore
;
590 WidgetCompositionEvent
endEvent(true, NS_COMPOSITION_END
, widget
);
591 endEvent
.data
= composition
->LastData();
592 endEvent
.mFlags
.mIsSynthesizedForTests
= true;
593 widget
->DispatchEvent(&endEvent
, status
);
597 case REQUEST_TO_CANCEL_COMPOSITION
: {
598 nsCOMPtr
<nsIWidget
> widget(aWidget
);
599 nsEventStatus status
= nsEventStatus_eIgnore
;
600 if (!composition
->LastData().IsEmpty()) {
601 WidgetCompositionEvent
updateEvent(true, NS_COMPOSITION_UPDATE
, widget
);
602 updateEvent
.data
= composition
->LastData();
603 updateEvent
.mFlags
.mIsSynthesizedForTests
= true;
604 widget
->DispatchEvent(&updateEvent
, status
);
605 if (widget
->Destroyed()) {
609 status
= nsEventStatus_eIgnore
;
610 WidgetTextEvent
textEvent(true, NS_TEXT_TEXT
, widget
);
611 textEvent
.theText
= composition
->LastData();
612 textEvent
.mFlags
.mIsSynthesizedForTests
= true;
613 widget
->DispatchEvent(&textEvent
, status
);
614 if (widget
->Destroyed()) {
619 status
= nsEventStatus_eIgnore
;
620 WidgetCompositionEvent
endEvent(true, NS_COMPOSITION_END
, widget
);
621 endEvent
.data
= composition
->LastData();
622 endEvent
.mFlags
.mIsSynthesizedForTests
= true;
623 widget
->DispatchEvent(&endEvent
, status
);
634 IMEStateManager::NotifyIME(IMEMessage aMessage
,
635 nsPresContext
* aPresContext
)
637 NS_ENSURE_TRUE(aPresContext
, NS_ERROR_INVALID_ARG
);
639 nsIWidget
* widget
= aPresContext
->GetRootWidget();
641 return NS_ERROR_NOT_AVAILABLE
;
643 return NotifyIME(aMessage
, widget
);
647 IMEStateManager::IsEditable(nsINode
* node
)
649 if (node
->IsEditable()) {
652 // |node| might be readwrite (for example, a text control)
653 if (node
->IsElement() &&
654 node
->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE
)) {
661 IMEStateManager::GetRootEditableNode(nsPresContext
* aPresContext
,
662 nsIContent
* aContent
)
665 nsINode
* root
= nullptr;
666 nsINode
* node
= aContent
;
667 while (node
&& IsEditable(node
)) {
669 node
= node
->GetParentNode();
674 nsIDocument
* document
= aPresContext
->Document();
675 if (document
&& document
->IsEditable()) {
683 IMEStateManager::IsEditableIMEState(nsIWidget
* aWidget
)
685 switch (aWidget
->GetInputContext().mIMEState
.mEnabled
) {
686 case IMEState::ENABLED
:
687 case IMEState::PASSWORD
:
689 case IMEState::PLUGIN
:
690 case IMEState::DISABLED
:
693 MOZ_CRASH("Unknown IME enable state");
698 IMEStateManager::DestroyTextStateManager()
700 if (!sActiveIMEContentObserver
) {
704 nsRefPtr
<IMEContentObserver
> tsm
;
705 tsm
.swap(sActiveIMEContentObserver
);
710 IMEStateManager::CreateIMEContentObserver()
712 if (sActiveIMEContentObserver
) {
713 NS_WARNING("text state observer has been there already");
714 MOZ_ASSERT(sActiveIMEContentObserver
->IsManaging(sPresContext
, sContent
));
718 nsCOMPtr
<nsIWidget
> widget
= sPresContext
->GetRootWidget();
720 return; // Sometimes, there are no widgets.
723 // If it's not text ediable, we don't need to create IMEContentObserver.
724 if (!IsEditableIMEState(widget
)) {
728 static bool sInitializeIsTestingIME
= true;
729 if (sInitializeIsTestingIME
) {
730 Preferences::AddBoolVarCache(&sIsTestingIME
, "test.IME", false);
731 sInitializeIsTestingIME
= false;
734 sActiveIMEContentObserver
= new IMEContentObserver();
735 NS_ADDREF(sActiveIMEContentObserver
);
737 // IMEContentObserver::Init() might create another IMEContentObserver
738 // instance. So, sActiveIMEContentObserver would be replaced with new one.
739 // We should hold the current instance here.
740 nsRefPtr
<IMEContentObserver
> kungFuDeathGrip(sActiveIMEContentObserver
);
741 sActiveIMEContentObserver
->Init(widget
, sPresContext
, sContent
);
745 IMEStateManager::GetFocusSelectionAndRoot(nsISelection
** aSelection
,
746 nsIContent
** aRootContent
)
748 if (!sActiveIMEContentObserver
) {
749 return NS_ERROR_NOT_AVAILABLE
;
751 return sActiveIMEContentObserver
->GetSelectionAndRoot(aSelection
,
756 already_AddRefed
<TextComposition
>
757 IMEStateManager::GetTextCompositionFor(nsIWidget
* aWidget
)
759 if (!sTextCompositions
) {
762 nsRefPtr
<TextComposition
> textComposition
=
763 sTextCompositions
->GetCompositionFor(aWidget
);
764 return textComposition
.forget();
768 already_AddRefed
<TextComposition
>
769 IMEStateManager::GetTextCompositionFor(WidgetGUIEvent
* aEvent
)
771 MOZ_ASSERT(aEvent
->AsCompositionEvent() || aEvent
->AsTextEvent() ||
772 aEvent
->AsKeyboardEvent(),
773 "aEvent has to be WidgetCompositionEvent, WidgetTextEvent or "
774 "WidgetKeyboardEvent");
775 return GetTextCompositionFor(aEvent
->widget
);
778 } // namespace mozilla