Bug 1032573 part 4 - Add AnimationTimeline::ToTimelineTime helper method; r=dbaron
[gecko.git] / dom / events / IMEStateManager.cpp
blobbc119a9136ee9be346b28affab04a48894083cf8
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"
20 #include "nsCOMPtr.h"
21 #include "nsContentUtils.h"
22 #include "nsIContent.h"
23 #include "nsIDocument.h"
24 #include "nsIDOMMouseEvent.h"
25 #include "nsIForm.h"
26 #include "nsIFormControl.h"
27 #include "nsINode.h"
28 #include "nsIObserverService.h"
29 #include "nsIPresShell.h"
30 #include "nsISelection.h"
31 #include "nsISupports.h"
32 #include "nsPresContext.h"
34 namespace mozilla {
36 using namespace dom;
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;
49 void
50 IMEStateManager::Shutdown()
52 MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
53 delete sTextCompositions;
54 sTextCompositions = nullptr;
57 nsresult
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) {
76 return NS_OK;
79 DestroyTextStateManager();
81 nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
82 if (widget) {
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;
90 return NS_OK;
93 nsresult
94 IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
95 nsIContent* aContent)
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();
110 if (widget) {
111 nsresult rv =
112 compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
113 if (NS_FAILED(rv)) {
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)) {
133 return NS_OK;
136 DestroyTextStateManager();
138 // Current IME transaction should commit
139 nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
140 if (widget) {
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;
150 return NS_OK;
153 nsresult
154 IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
155 nsIContent* aContent,
156 InputContextAction::Cause aCause)
158 InputContextAction action(aCause);
159 return OnChangeFocusInternal(aPresContext, aContent, action);
162 nsresult
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.
175 if (aPresContext) {
176 NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
180 if (sActiveIMEContentObserver &&
181 (aPresContext || !sActiveIMEContentObserver->KeepAliveDuringDeactive()) &&
182 !sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
183 DestroyTextStateManager();
186 if (!aPresContext) {
187 return NS_OK;
190 nsCOMPtr<nsIWidget> widget =
191 (sPresContext == aPresContext) ? oldWidget.get() :
192 aPresContext->GetRootWidget();
193 if (!widget) {
194 return NS_OK;
197 IMEState newState = GetNewIMEState(aPresContext, aContent);
198 if (!focusActuallyChanging) {
199 // actual focus isn't changing, but if IME enabled state is changing,
200 // we should do it.
201 InputContext context = widget->GetInputContext();
202 if (context.mIMEState.mEnabled == newState.mEnabled) {
203 // the enabled state isn't changing.
204 return NS_OK;
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
215 // focus.
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.
233 return NS_OK;
236 void
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);
247 void
248 IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
249 nsIContent* aContent,
250 nsIDOMMouseEvent* aMouseEvent)
252 if (sPresContext != aPresContext || sContent != aContent) {
253 return;
256 nsCOMPtr<nsIWidget> widget = aPresContext->GetRootWidget();
257 NS_ENSURE_TRUE_VOID(widget);
259 bool isTrusted;
260 nsresult rv = aMouseEvent->GetIsTrusted(&isTrusted);
261 NS_ENSURE_SUCCESS_VOID(rv);
262 if (!isTrusted) {
263 return; // ignore untrusted event.
266 int16_t button;
267 rv = aMouseEvent->GetButton(&button);
268 NS_ENSURE_SUCCESS_VOID(rv);
269 if (button != 0) {
270 return; // not a left click event.
273 int32_t clickCount;
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);
286 void
287 IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
288 nsIContent* aContent)
290 if (sPresContext != aPresContext || sContent != aContent) {
291 return;
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)) {
298 return;
300 DestroyTextStateManager();
303 CreateIMEContentObserver();
306 void
307 IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
308 nsIContent* aContent)
310 if (!sPresContext) {
311 NS_WARNING("ISM doesn't know which editor has focus");
312 return;
314 nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
315 if (!widget) {
316 NS_WARNING("focused widget is not found");
317 return;
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();
350 IMEState
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);
364 if (!aContent) {
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 {
379 public:
380 IMEEnabledStateChangedEvent(uint32_t aState)
381 : mState(aState)
385 NS_IMETHOD Run()
387 nsCOMPtr<nsIObserverService> observerService =
388 services::GetObserverService();
389 if (observerService) {
390 nsAutoString state;
391 state.AppendInt(mState);
392 observerService->NotifyObservers(nullptr, "ime-enabled-state-changed",
393 state.get());
395 return NS_OK;
398 private:
399 uint32_t mState;
402 void
403 IMEStateManager::SetIMEState(const IMEState& aState,
404 nsIContent* aContent,
405 nsIWidget* aWidget,
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);
426 if (inputElement) {
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);
435 } else {
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;
453 if (control) {
454 // is this a form and does it have a default submit element?
455 if ((form = do_QueryInterface(formElement)) &&
456 form->GetDefaultSubmitElement()) {
457 willSubmit = true;
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()) {
463 willSubmit = true;
466 context.mActionHint.Assign(
467 willSubmit ? (control->GetType() == NS_FORM_INPUT_SEARCH ?
468 NS_LITERAL_STRING("search") : NS_LITERAL_STRING("go")) :
469 (formElement ?
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) {
483 return;
486 nsContentUtils::AddScriptRunner(
487 new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled));
490 void
491 IMEStateManager::EnsureTextCompositionArray()
493 if (sTextCompositions) {
494 return;
496 sTextCompositions = new TextCompositionArray();
499 void
500 IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
501 nsPresContext* aPresContext,
502 WidgetEvent* aEvent,
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) {
509 return;
512 EnsureTextCompositionArray();
514 WidgetGUIEvent* GUIEvent = aEvent->AsGUIEvent();
516 nsRefPtr<TextComposition> composition =
517 sTextCompositions->GetCompositionFor(GUIEvent->widget);
518 if (!composition) {
519 MOZ_ASSERT(GUIEvent->message == NS_COMPOSITION_START);
520 composition = new TextComposition(aPresContext, aEventTargetNode, GUIEvent);
521 sTextCompositions->AppendElement(composition);
523 #ifdef DEBUG
524 else {
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);
545 // static
546 nsresult
547 IMEStateManager::NotifyIME(IMEMessage aMessage,
548 nsIWidget* aWidget)
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()) {
557 switch (aMessage) {
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:
563 return composition ?
564 aWidget->NotifyIME(IMENotification(aMessage)) : NS_OK;
565 default:
566 MOZ_CRASH("Unsupported notification");
568 MOZ_CRASH(
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.
575 switch (aMessage) {
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()) {
585 return NS_OK;
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);
595 return NS_OK;
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()) {
606 return NS_OK;
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()) {
615 return NS_OK;
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);
625 return NS_OK;
627 default:
628 return NS_OK;
632 // static
633 nsresult
634 IMEStateManager::NotifyIME(IMEMessage aMessage,
635 nsPresContext* aPresContext)
637 NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
639 nsIWidget* widget = aPresContext->GetRootWidget();
640 if (!widget) {
641 return NS_ERROR_NOT_AVAILABLE;
643 return NotifyIME(aMessage, widget);
646 bool
647 IMEStateManager::IsEditable(nsINode* node)
649 if (node->IsEditable()) {
650 return true;
652 // |node| might be readwrite (for example, a text control)
653 if (node->IsElement() &&
654 node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
655 return true;
657 return false;
660 nsINode*
661 IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
662 nsIContent* aContent)
664 if (aContent) {
665 nsINode* root = nullptr;
666 nsINode* node = aContent;
667 while (node && IsEditable(node)) {
668 root = node;
669 node = node->GetParentNode();
671 return root;
673 if (aPresContext) {
674 nsIDocument* document = aPresContext->Document();
675 if (document && document->IsEditable()) {
676 return document;
679 return nullptr;
682 bool
683 IMEStateManager::IsEditableIMEState(nsIWidget* aWidget)
685 switch (aWidget->GetInputContext().mIMEState.mEnabled) {
686 case IMEState::ENABLED:
687 case IMEState::PASSWORD:
688 return true;
689 case IMEState::PLUGIN:
690 case IMEState::DISABLED:
691 return false;
692 default:
693 MOZ_CRASH("Unknown IME enable state");
697 void
698 IMEStateManager::DestroyTextStateManager()
700 if (!sActiveIMEContentObserver) {
701 return;
704 nsRefPtr<IMEContentObserver> tsm;
705 tsm.swap(sActiveIMEContentObserver);
706 tsm->Destroy();
709 void
710 IMEStateManager::CreateIMEContentObserver()
712 if (sActiveIMEContentObserver) {
713 NS_WARNING("text state observer has been there already");
714 MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
715 return;
718 nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
719 if (!widget) {
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)) {
725 return;
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);
744 nsresult
745 IMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection,
746 nsIContent** aRootContent)
748 if (!sActiveIMEContentObserver) {
749 return NS_ERROR_NOT_AVAILABLE;
751 return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
752 aRootContent);
755 // static
756 already_AddRefed<TextComposition>
757 IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
759 if (!sTextCompositions) {
760 return nullptr;
762 nsRefPtr<TextComposition> textComposition =
763 sTextCompositions->GetCompositionFor(aWidget);
764 return textComposition.forget();
767 // static
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