dom/events/IMEStateManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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()
    51 {
    52   MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
    53   delete sTextCompositions;
    54   sTextCompositions = nullptr;
    55 }
    57 nsresult
    58 IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
    59 {
    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);
    72     }
    73   }
    75   if (aPresContext != sPresContext) {
    76     return NS_OK;
    77   }
    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);
    87   }
    88   NS_IF_RELEASE(sContent);
    89   sPresContext = nullptr;
    90   return NS_OK;
    91 }
    93 nsresult
    94 IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
    95                                  nsIContent* aContent)
    96 {
    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);
   115         }
   116         // By calling the APIs, the composition may have been finished normally.
   117         compositionInContent =
   118           sTextCompositions->GetCompositionFor(
   119                                compositionInContent->GetPresContext(),
   120                                compositionInContent->GetEventTargetNode());
   121       }
   122     }
   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);
   128     }
   129   }
   131   if (!sPresContext || !sContent ||
   132       !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
   133     return NS_OK;
   134   }
   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);
   145   }
   147   NS_IF_RELEASE(sContent);
   148   sPresContext = nullptr;
   150   return NS_OK;
   151 }
   153 nsresult
   154 IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
   155                                nsIContent* aContent,
   156                                InputContextAction::Cause aCause)
   157 {
   158   InputContextAction action(aCause);
   159   return OnChangeFocusInternal(aPresContext, aContent, action);
   160 }
   162 nsresult
   163 IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
   164                                        nsIContent* aContent,
   165                                        InputContextAction aAction)
   166 {
   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);
   177     }
   178   }
   180   if (sActiveIMEContentObserver &&
   181       (aPresContext || !sActiveIMEContentObserver->KeepAliveDuringDeactive()) &&
   182       !sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
   183     DestroyTextStateManager();
   184   }
   186   if (!aPresContext) {
   187     return NS_OK;
   188   }
   190   nsCOMPtr<nsIWidget> widget =
   191     (sPresContext == aPresContext) ? oldWidget.get() :
   192                                      aPresContext->GetRootWidget();
   193   if (!widget) {
   194     return NS_OK;
   195   }
   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;
   205     }
   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);
   212     }
   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;
   219   }
   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);
   228   }
   230   // Don't call CreateIMEContentObserver() here, it should be called from
   231   // focus event handler of editor.
   233   return NS_OK;
   234 }
   236 void
   237 IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling)
   238 {
   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);
   245 }
   247 void
   248 IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
   249                                  nsIContent* aContent,
   250                                  nsIDOMMouseEvent* aMouseEvent)
   251 {
   252   if (sPresContext != aPresContext || sContent != aContent) {
   253     return;
   254   }
   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.
   264   }
   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.
   271   }
   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.
   278   }
   280   InputContextAction action(InputContextAction::CAUSE_MOUSE,
   281                             InputContextAction::FOCUS_NOT_CHANGED);
   282   IMEState newState = GetNewIMEState(aPresContext, aContent);
   283   SetIMEState(newState, aContent, widget, action);
   284 }
   286 void
   287 IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
   288                                  nsIContent* aContent)
   289 {
   290   if (sPresContext != aPresContext || sContent != aContent) {
   291     return;
   292   }
   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;
   299     }
   300     DestroyTextStateManager();
   301   }
   303   CreateIMEContentObserver();
   304 }
   306 void
   307 IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
   308                                 nsIContent* aContent)
   309 {
   310   if (!sPresContext) {
   311     NS_WARNING("ISM doesn't know which editor has focus");
   312     return;
   313   }
   314   nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   315   if (!widget) {
   316     NS_WARNING("focused widget is not found");
   317     return;
   318   }
   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);
   333   }
   335   if (createTextStateManager) {
   336     DestroyTextStateManager();
   337   }
   339   if (updateIMEState) {
   340     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
   341                               InputContextAction::FOCUS_NOT_CHANGED);
   342     SetIMEState(aNewIMEState, aContent, widget, action);
   343   }
   345   if (createTextStateManager) {
   346     CreateIMEContentObserver();
   347   }
   348 }
   350 IMEState
   351 IMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
   352                                 nsIContent*    aContent)
   353 {
   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);
   358   }
   360   if (sInstalledMenuKeyboardListener) {
   361     return IMEState(IMEState::DISABLED);
   362   }
   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);
   370     }
   371     return IMEState(IMEState::DISABLED);
   372   }
   374   return aContent->GetDesiredIMEState();
   375 }
   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)
   382   {
   383   }
   385   NS_IMETHOD Run()
   386   {
   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());
   394     }
   395     return NS_OK;
   396   }
   398 private:
   399   uint32_t mState;
   400 };
   402 void
   403 IMEStateManager::SetIMEState(const IMEState& aState,
   404                              nsIContent* aContent,
   405                              nsIWidget* aWidget,
   406                              InputContextAction aAction)
   407 {
   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>
   431         }
   432       }
   433       content->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
   434                        context.mHTMLInputType);
   435     } else {
   436       context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
   437     }
   439     if (Preferences::GetBool("dom.forms.inputmode", false)) {
   440       aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
   441                         context.mHTMLInputInputmode);
   442     }
   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;
   464         }
   465       }
   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()));
   471     }
   472   }
   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;
   479   }
   481   aWidget->SetInputContext(context, aAction);
   482   if (oldContext.mIMEState.mEnabled == context.mIMEState.mEnabled) {
   483     return;
   484   }
   486   nsContentUtils::AddScriptRunner(
   487     new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled));
   488 }
   490 void
   491 IMEStateManager::EnsureTextCompositionArray()
   492 {
   493   if (sTextCompositions) {
   494     return;
   495   }
   496   sTextCompositions = new TextCompositionArray();
   497 }
   499 void
   500 IMEStateManager::DispatchCompositionEvent(nsINode* aEventTargetNode,
   501                                           nsPresContext* aPresContext,
   502                                           WidgetEvent* aEvent,
   503                                           nsEventStatus* aStatus,
   504                                           EventDispatchingCallback* aCallBack)
   505 {
   506   MOZ_ASSERT(aEvent->eventStructType == NS_COMPOSITION_EVENT ||
   507              aEvent->eventStructType == NS_TEXT_EVENT);
   508   if (!aEvent->mFlags.mIsTrusted || aEvent->mFlags.mPropagationStopped) {
   509     return;
   510   }
   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);
   522   }
   523 #ifdef DEBUG
   524   else {
   525     MOZ_ASSERT(GUIEvent->message != NS_COMPOSITION_START);
   526   }
   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);
   541     }
   542   }
   543 }
   545 // static
   546 nsresult
   547 IMEStateManager::NotifyIME(IMEMessage aMessage,
   548                            nsIWidget* aWidget)
   549 {
   550   NS_ENSURE_TRUE(aWidget, NS_ERROR_INVALID_ARG);
   552   nsRefPtr<TextComposition> composition;
   553   if (sTextCompositions) {
   554     composition = sTextCompositions->GetCompositionFor(aWidget);
   555   }
   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");
   567     }
   568     MOZ_CRASH(
   569       "Failed to handle the notification for non-synthesized composition");
   570   }
   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;
   586         }
   587       }
   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;
   596     }
   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;
   607         }
   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;
   616         }
   617       }
   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;
   626     }
   627     default:
   628       return NS_OK;
   629   }
   630 }
   632 // static
   633 nsresult
   634 IMEStateManager::NotifyIME(IMEMessage aMessage,
   635                            nsPresContext* aPresContext)
   636 {
   637   NS_ENSURE_TRUE(aPresContext, NS_ERROR_INVALID_ARG);
   639   nsIWidget* widget = aPresContext->GetRootWidget();
   640   if (!widget) {
   641     return NS_ERROR_NOT_AVAILABLE;
   642   }
   643   return NotifyIME(aMessage, widget);
   644 }
   646 bool
   647 IMEStateManager::IsEditable(nsINode* node)
   648 {
   649   if (node->IsEditable()) {
   650     return true;
   651   }
   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;
   656   }
   657   return false;
   658 }
   660 nsINode*
   661 IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
   662                                      nsIContent* aContent)
   663 {
   664   if (aContent) {
   665     nsINode* root = nullptr;
   666     nsINode* node = aContent;
   667     while (node && IsEditable(node)) {
   668       root = node;
   669       node = node->GetParentNode();
   670     }
   671     return root;
   672   }
   673   if (aPresContext) {
   674     nsIDocument* document = aPresContext->Document();
   675     if (document && document->IsEditable()) {
   676       return document;
   677     }
   678   }
   679   return nullptr;
   680 }
   682 bool
   683 IMEStateManager::IsEditableIMEState(nsIWidget* aWidget)
   684 {
   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");
   694   }
   695 }
   697 void
   698 IMEStateManager::DestroyTextStateManager()
   699 {
   700   if (!sActiveIMEContentObserver) {
   701     return;
   702   }
   704   nsRefPtr<IMEContentObserver> tsm;
   705   tsm.swap(sActiveIMEContentObserver);
   706   tsm->Destroy();
   707 }
   709 void
   710 IMEStateManager::CreateIMEContentObserver()
   711 {
   712   if (sActiveIMEContentObserver) {
   713     NS_WARNING("text state observer has been there already");
   714     MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
   715     return;
   716   }
   718   nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
   719   if (!widget) {
   720     return; // Sometimes, there are no widgets.
   721   }
   723   // If it's not text ediable, we don't need to create IMEContentObserver.
   724   if (!IsEditableIMEState(widget)) {
   725     return;
   726   }
   728   static bool sInitializeIsTestingIME = true;
   729   if (sInitializeIsTestingIME) {
   730     Preferences::AddBoolVarCache(&sIsTestingIME, "test.IME", false);
   731     sInitializeIsTestingIME = false;
   732   }
   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);
   742 }
   744 nsresult
   745 IMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection,
   746                                           nsIContent** aRootContent)
   747 {
   748   if (!sActiveIMEContentObserver) {
   749     return NS_ERROR_NOT_AVAILABLE;
   750   }
   751   return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
   752                                                         aRootContent);
   753 }
   755 // static
   756 already_AddRefed<TextComposition>
   757 IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
   758 {
   759   if (!sTextCompositions) {
   760     return nullptr;
   761   }
   762   nsRefPtr<TextComposition> textComposition =
   763     sTextCompositions->GetCompositionFor(aWidget);
   764   return textComposition.forget();
   765 }
   767 // static
   768 already_AddRefed<TextComposition>
   769 IMEStateManager::GetTextCompositionFor(WidgetGUIEvent* aEvent)
   770 {
   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);
   776 }
   778 } // namespace mozilla

mercurial