forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1999 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1999 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/Logging.h"
 | |
| 
 | |
| #include "mozilla/IMEStateManager.h"
 | |
| 
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/EditorBase.h"
 | |
| #include "mozilla/EventListenerManager.h"
 | |
| #include "mozilla/EventStates.h"
 | |
| #include "mozilla/MouseEvents.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/Services.h"
 | |
| #include "mozilla/TextComposition.h"
 | |
| #include "mozilla/TextEvents.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "mozilla/dom/HTMLFormElement.h"
 | |
| #include "mozilla/dom/TabParent.h"
 | |
| 
 | |
| #include "HTMLInputElement.h"
 | |
| #include "IMEContentObserver.h"
 | |
| 
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsIDocument.h"
 | |
| #include "nsIDOMMouseEvent.h"
 | |
| #include "nsIForm.h"
 | |
| #include "nsIFormControl.h"
 | |
| #include "nsINode.h"
 | |
| #include "nsIObserverService.h"
 | |
| #include "nsIPresShell.h"
 | |
| #include "nsISelection.h"
 | |
| #include "nsISupports.h"
 | |
| #include "nsPresContext.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| using namespace dom;
 | |
| using namespace widget;
 | |
| 
 | |
| /**
 | |
|  * When a method is called, log its arguments and/or related static variables
 | |
|  * with LogLevel::Info.  However, if it puts too many logs like
 | |
|  * OnDestroyPresContext(), should long only when the method actually does
 | |
|  * something. In this case, the log should start with "<method name>".
 | |
|  *
 | |
|  * When a method quits due to unexpected situation, log the reason with
 | |
|  * LogLevel::Error.  In this case, the log should start with
 | |
|  * "<method name>(), FAILED".  The indent makes the log look easier.
 | |
|  *
 | |
|  * When a method does something only in some situations and it may be important
 | |
|  * for debug, log the information with LogLevel::Debug.  In this case, the log
 | |
|  * should start with "  <method name>(),".
 | |
|  */
 | |
| LazyLogModule sISMLog("IMEStateManager");
 | |
| 
 | |
| static const char*
 | |
| GetBoolName(bool aBool)
 | |
| {
 | |
|   return aBool ? "true" : "false";
 | |
| }
 | |
| 
 | |
| static const char*
 | |
| GetActionCauseName(InputContextAction::Cause aCause)
 | |
| {
 | |
|   switch (aCause) {
 | |
|     case InputContextAction::CAUSE_UNKNOWN:
 | |
|       return "CAUSE_UNKNOWN";
 | |
|     case InputContextAction::CAUSE_UNKNOWN_CHROME:
 | |
|       return "CAUSE_UNKNOWN_CHROME";
 | |
|     case InputContextAction::CAUSE_KEY:
 | |
|       return "CAUSE_KEY";
 | |
|     case InputContextAction::CAUSE_MOUSE:
 | |
|       return "CAUSE_MOUSE";
 | |
|     case InputContextAction::CAUSE_TOUCH:
 | |
|       return "CAUSE_TOUCH";
 | |
|     default:
 | |
|       return "illegal value";
 | |
|   }
 | |
| }
 | |
| 
 | |
| static const char*
 | |
| GetActionFocusChangeName(InputContextAction::FocusChange aFocusChange)
 | |
| {
 | |
|   switch (aFocusChange) {
 | |
|     case InputContextAction::FOCUS_NOT_CHANGED:
 | |
|       return "FOCUS_NOT_CHANGED";
 | |
|     case InputContextAction::GOT_FOCUS:
 | |
|       return "GOT_FOCUS";
 | |
|     case InputContextAction::LOST_FOCUS:
 | |
|       return "LOST_FOCUS";
 | |
|     case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
 | |
|       return "MENU_GOT_PSEUDO_FOCUS";
 | |
|     case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
 | |
|       return "MENU_LOST_PSEUDO_FOCUS";
 | |
|     default:
 | |
|       return "illegal value";
 | |
|   }
 | |
| }
 | |
| 
 | |
| static const char*
 | |
| GetIMEStateEnabledName(IMEState::Enabled aEnabled)
 | |
| {
 | |
|   switch (aEnabled) {
 | |
|     case IMEState::DISABLED:
 | |
|       return "DISABLED";
 | |
|     case IMEState::ENABLED:
 | |
|       return "ENABLED";
 | |
|     case IMEState::PASSWORD:
 | |
|       return "PASSWORD";
 | |
|     case IMEState::PLUGIN:
 | |
|       return "PLUGIN";
 | |
|     default:
 | |
|       return "illegal value";
 | |
|   }
 | |
| }
 | |
| 
 | |
| static const char*
 | |
| GetIMEStateSetOpenName(IMEState::Open aOpen)
 | |
| {
 | |
|   switch (aOpen) {
 | |
|     case IMEState::DONT_CHANGE_OPEN_STATE:
 | |
|       return "DONT_CHANGE_OPEN_STATE";
 | |
|     case IMEState::OPEN:
 | |
|       return "OPEN";
 | |
|     case IMEState::CLOSED:
 | |
|       return "CLOSED";
 | |
|     default:
 | |
|       return "illegal value";
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool
 | |
| IsSameProcess(const TabParent* aTabParent1, const TabParent* aTabParent2)
 | |
| {
 | |
|   if (aTabParent1 == aTabParent2) {
 | |
|     return true;
 | |
|   }
 | |
|   if (!aTabParent1 != !aTabParent2) {
 | |
|     return false;
 | |
|   }
 | |
|   return aTabParent1->Manager() == aTabParent2->Manager();
 | |
| }
 | |
| 
 | |
| StaticRefPtr<nsIContent> IMEStateManager::sContent;
 | |
| StaticRefPtr<nsPresContext> IMEStateManager::sPresContext;
 | |
| nsIWidget* IMEStateManager::sWidget = nullptr;
 | |
| nsIWidget* IMEStateManager::sFocusedIMEWidget = nullptr;
 | |
| StaticRefPtr<TabParent> IMEStateManager::sFocusedIMETabParent;
 | |
| nsIWidget* IMEStateManager::sActiveInputContextWidget = nullptr;
 | |
| StaticRefPtr<TabParent> IMEStateManager::sActiveTabParent;
 | |
| StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
 | |
| TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
 | |
| InputContext::Origin IMEStateManager::sOrigin = InputContext::ORIGIN_MAIN;
 | |
| InputContext IMEStateManager::sActiveChildInputContext;
 | |
| bool IMEStateManager::sInstalledMenuKeyboardListener = false;
 | |
| bool IMEStateManager::sIsGettingNewIMEState = false;
 | |
| bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
 | |
| bool IMEStateManager::sInputModeSupported = false;
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::Init()
 | |
| {
 | |
|   Preferences::AddBoolVarCache(
 | |
|     &sCheckForIMEUnawareWebApps,
 | |
|     "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
 | |
|     false);
 | |
| 
 | |
|   Preferences::AddBoolVarCache(
 | |
|     &sInputModeSupported,
 | |
|     "dom.forms.inputmode",
 | |
|     false);
 | |
| 
 | |
|   sOrigin = XRE_IsParentProcess() ? InputContext::ORIGIN_MAIN :
 | |
|                                     InputContext::ORIGIN_CONTENT;
 | |
|   ResetActiveChildInputContext();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::Shutdown()
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("Shutdown(), sTextCompositions=0x%p, sTextCompositions->Length()=%zu",
 | |
|      sTextCompositions, sTextCompositions ? sTextCompositions->Length() : 0));
 | |
| 
 | |
|   MOZ_ASSERT(!sTextCompositions || !sTextCompositions->Length());
 | |
|   delete sTextCompositions;
 | |
|   sTextCompositions = nullptr;
 | |
|   // All string instances in the global space need to be empty after XPCOM
 | |
|   // shutdown.
 | |
|   sActiveChildInputContext.ShutDown();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnTabParentDestroying(TabParent* aTabParent)
 | |
| {
 | |
|   if (sFocusedIMETabParent == aTabParent) {
 | |
|     NotifyIMEOfBlurForChildProcess();
 | |
|   }
 | |
| 
 | |
|   if (sActiveTabParent != aTabParent) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnTabParentDestroying(aTabParent=0x%p), "
 | |
|      "The active TabParent is being destroyed", aTabParent));
 | |
| 
 | |
|   // The active remote process might have crashed.
 | |
|   sActiveTabParent = nullptr;
 | |
| 
 | |
|   // XXX: Need to disable IME?
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::WidgetDestroyed(nsIWidget* aWidget)
 | |
| {
 | |
|   if (sWidget == aWidget) {
 | |
|     sWidget = nullptr;
 | |
|   }
 | |
|   if (sFocusedIMEWidget == aWidget) {
 | |
|     NotifyIMEOfBlurForChildProcess();
 | |
|     sFocusedIMEWidget = nullptr;
 | |
|   }
 | |
|   if (sActiveInputContextWidget == aWidget) {
 | |
|     sActiveInputContextWidget = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::StopIMEStateManagement()
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("StopIMEStateManagement()"));
 | |
| 
 | |
|   // NOTE: Don't set input context from here since this has already lost
 | |
|   //       the rights to change input context.
 | |
| 
 | |
|   if (sTextCompositions && sPresContext) {
 | |
|     NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext, sActiveTabParent);
 | |
|   }
 | |
|   sActiveInputContextWidget = nullptr;
 | |
|   sPresContext = nullptr;
 | |
|   sContent = nullptr;
 | |
|   sActiveTabParent = nullptr;
 | |
|   DestroyIMEContentObserver();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::NotifyIMEOfBlurForChildProcess()
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|     ("NotifyIMEOfBlurForChildProcess(), sFocusedIMETabParent=0x%p, "
 | |
|      "sFocusedIMEWidget=0x%p",
 | |
|      sFocusedIMETabParent.get(), sFocusedIMEWidget));
 | |
| 
 | |
|   if (!sFocusedIMETabParent) {
 | |
|     MOZ_ASSERT(!sFocusedIMEWidget);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(sFocusedIMEWidget);
 | |
| 
 | |
|   if (MOZ_LOG_TEST(sISMLog, LogLevel::Debug) && sTextCompositions) {
 | |
|     RefPtr<TextComposition> composition =
 | |
|       sTextCompositions->GetCompositionFor(sFocusedIMEWidget);
 | |
|     if (composition) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  NotifyIMEOfBlurForChildProcess(), sFocusedIMEWidget still has "
 | |
|          "composition"));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMETabParent);
 | |
| 
 | |
|   MOZ_ASSERT(!sFocusedIMETabParent);
 | |
|   MOZ_ASSERT(!sFocusedIMEWidget);
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::MaybeStartOffsetUpdatedInChild(nsIWidget* aWidget,
 | |
|                                                 uint32_t aStartOffset)
 | |
| {
 | |
|   if (NS_WARN_IF(!sTextCompositions)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|       ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
 | |
|        "called when there is no composition", aWidget, aStartOffset));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<TextComposition> composition = GetTextCompositionFor(aWidget);
 | |
|   if (NS_WARN_IF(!composition)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|       ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
 | |
|        "called when there is no composition", aWidget, aStartOffset));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (composition->NativeOffsetOfStartComposition() == aStartOffset) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("MaybeStartOffsetUpdatedInChild(aWidget=0x%p, aStartOffset=%u), "
 | |
|      "old offset=%u",
 | |
|      aWidget, aStartOffset, composition->NativeOffsetOfStartComposition()));
 | |
|   composition->OnStartOffsetUpdatedInChild(aStartOffset);
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aPresContext);
 | |
| 
 | |
|   // First, if there is a composition in the aPresContext, clean up it.
 | |
|   if (sTextCompositions) {
 | |
|     TextCompositionArray::index_type i =
 | |
|       sTextCompositions->IndexOf(aPresContext);
 | |
|     if (i != TextCompositionArray::NoIndex) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  OnDestroyPresContext(), "
 | |
|          "removing TextComposition instance from the array (index=%zu)", i));
 | |
|       // there should be only one composition per presContext object.
 | |
|       sTextCompositions->ElementAt(i)->Destroy();
 | |
|       sTextCompositions->RemoveElementAt(i);
 | |
|       if (sTextCompositions->IndexOf(aPresContext) !=
 | |
|             TextCompositionArray::NoIndex) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|           ("  OnDestroyPresContext(), FAILED to remove "
 | |
|            "TextComposition instance from the array"));
 | |
|         MOZ_CRASH("Failed to remove TextComposition instance from the array");
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aPresContext != sPresContext) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnDestroyPresContext(aPresContext=0x%p), "
 | |
|      "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
 | |
|      aPresContext, sPresContext.get(), sContent.get(), sTextCompositions));
 | |
| 
 | |
|   DestroyIMEContentObserver();
 | |
| 
 | |
|   if (sWidget) {
 | |
|     IMEState newState = GetNewIMEState(sPresContext, nullptr);
 | |
|     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
 | |
|                               InputContextAction::LOST_FOCUS);
 | |
|     InputContext::Origin origin =
 | |
|       sActiveTabParent ? InputContext::ORIGIN_CONTENT : sOrigin;
 | |
|     SetIMEState(newState, nullptr, nullptr, sWidget, action, origin);
 | |
|   }
 | |
|   sWidget = nullptr;
 | |
|   sContent = nullptr;
 | |
|   sPresContext = nullptr;
 | |
|   sActiveTabParent = nullptr;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
 | |
|                                  nsIContent* aContent)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aPresContext);
 | |
| 
 | |
|   // First, if there is a composition in the aContent, clean up it.
 | |
|   if (sTextCompositions) {
 | |
|     RefPtr<TextComposition> compositionInContent =
 | |
|       sTextCompositions->GetCompositionInContent(aPresContext, aContent);
 | |
| 
 | |
|     if (compositionInContent) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  OnRemoveContent(), "
 | |
|          "composition is in the content"));
 | |
| 
 | |
|       // Try resetting the native IME state.  Be aware, typically, this method
 | |
|       // is called during the content being removed.  Then, the native
 | |
|       // composition events which are caused by following APIs are ignored due
 | |
|       // to unsafe to run script (in PresShell::HandleEvent()).
 | |
|       nsresult rv =
 | |
|         compositionInContent->NotifyIME(REQUEST_TO_CANCEL_COMPOSITION);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         compositionInContent->NotifyIME(REQUEST_TO_COMMIT_COMPOSITION);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!sPresContext || !sContent ||
 | |
|       !nsContentUtils::ContentIsDescendantOf(sContent, aContent)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnRemoveContent(aPresContext=0x%p, aContent=0x%p), "
 | |
|      "sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
 | |
|      aPresContext, aContent, sPresContext.get(), sContent.get(), sTextCompositions));
 | |
| 
 | |
|   DestroyIMEContentObserver();
 | |
| 
 | |
|   // Current IME transaction should commit
 | |
|   if (sWidget) {
 | |
|     IMEState newState = GetNewIMEState(sPresContext, nullptr);
 | |
|     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
 | |
|                               InputContextAction::LOST_FOCUS);
 | |
|     InputContext::Origin origin =
 | |
|       sActiveTabParent ? InputContext::ORIGIN_CONTENT : sOrigin;
 | |
|     SetIMEState(newState, aPresContext, nullptr, sWidget, action, origin);
 | |
|   }
 | |
| 
 | |
|   sWidget = nullptr;
 | |
|   sContent = nullptr;
 | |
|   sPresContext = nullptr;
 | |
|   sActiveTabParent = nullptr;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| IMEStateManager::CanHandleWith(nsPresContext* aPresContext)
 | |
| {
 | |
|   return aPresContext &&
 | |
|          aPresContext->GetPresShell() &&
 | |
|          !aPresContext->PresShell()->IsDestroying();
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::OnChangeFocus(nsPresContext* aPresContext,
 | |
|                                nsIContent* aContent,
 | |
|                                InputContextAction::Cause aCause)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnChangeFocus(aPresContext=0x%p, aContent=0x%p, aCause=%s)",
 | |
|      aPresContext, aContent, GetActionCauseName(aCause)));
 | |
| 
 | |
|   InputContextAction action(aCause);
 | |
|   return OnChangeFocusInternal(aPresContext, aContent, action);
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
 | |
|                                        nsIContent* aContent,
 | |
|                                        InputContextAction aAction)
 | |
| {
 | |
|   RefPtr<TabParent> newTabParent = TabParent::GetFrom(aContent);
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnChangeFocusInternal(aPresContext=0x%p (available: %s), "
 | |
|      "aContent=0x%p (TabParent=0x%p), aAction={ mCause=%s, mFocusChange=%s }), "
 | |
|      "sPresContext=0x%p (available: %s), sContent=0x%p, "
 | |
|      "sWidget=0x%p (available: %s), sActiveTabParent=0x%p, "
 | |
|      "sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s",
 | |
|      aPresContext, GetBoolName(CanHandleWith(aPresContext)), aContent,
 | |
|      newTabParent.get(), GetActionCauseName(aAction.mCause),
 | |
|      GetActionFocusChangeName(aAction.mFocusChange),
 | |
|      sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)),
 | |
|      sContent.get(), sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
 | |
|      sActiveTabParent.get(), sActiveIMEContentObserver.get(),
 | |
|      GetBoolName(sInstalledMenuKeyboardListener)));
 | |
| 
 | |
|   // If new aPresShell has been destroyed, this should handle the focus change
 | |
|   // as nobody is getting focus.
 | |
|   if (NS_WARN_IF(aPresContext && !CanHandleWith(aPresContext))) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|       ("  OnChangeFocusInternal(), called with destroyed PresShell, "
 | |
|        "handling this call as nobody getting focus"));
 | |
|     aPresContext = nullptr;
 | |
|     aContent = nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> oldWidget = sWidget;
 | |
|   nsCOMPtr<nsIWidget> newWidget =
 | |
|     aPresContext ? aPresContext->GetRootWidget() : nullptr;
 | |
|   bool focusActuallyChanging =
 | |
|     (sContent != aContent || sPresContext != aPresContext ||
 | |
|      oldWidget != newWidget || sActiveTabParent != newTabParent);
 | |
| 
 | |
|   // If old widget has composition, we may need to commit composition since
 | |
|   // a native IME context is shared on all editors on some widgets or all
 | |
|   // widgets (it depends on platforms).
 | |
|   if (oldWidget && focusActuallyChanging && sTextCompositions) {
 | |
|     RefPtr<TextComposition> composition =
 | |
|       sTextCompositions->GetCompositionFor(oldWidget);
 | |
|     if (composition) {
 | |
|       // However, don't commit the composition if we're being inactivated
 | |
|       // but the composition should be kept even during deactive.
 | |
|       // Note that oldWidget and sFocusedIMEWidget may be different here (in
 | |
|       // such case, sFocusedIMEWidget is perhaps nullptr).  For example, IME
 | |
|       // may receive only blur notification but still has composition.
 | |
|       // We need to clean up only the oldWidget's composition state here.
 | |
|       if (aPresContext ||
 | |
|           !oldWidget->IMENotificationRequestsRef().WantDuringDeactive()) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|           ("  OnChangeFocusInternal(), requesting to commit composition to "
 | |
|            "the (previous) focused widget"));
 | |
|         NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
 | |
|                   composition->GetTabParent());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (sActiveIMEContentObserver) {
 | |
|     // If there is active IMEContentObserver, it means that focused content was
 | |
|     // in this process.  So, if a tab parent gets focus, it means that the
 | |
|     // focused editor in this process is being blurred.
 | |
|     if (newTabParent) {
 | |
|       DestroyIMEContentObserver();
 | |
|     }
 | |
|     // If the process is being inactivated, then, IMEContentObserver should
 | |
|     // stop observing the contents unless native IME requests to keep
 | |
|     // composition even during deactivated.
 | |
|     else if (!aPresContext) {
 | |
|       if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) {
 | |
|         DestroyIMEContentObserver();
 | |
|       }
 | |
|     }
 | |
|     // Otherwise, i.e., new focused content is in this process, let's check
 | |
|     // whether the new focused content is already being managed by the
 | |
|     // active IME content observer.
 | |
|     else if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
 | |
|       DestroyIMEContentObserver();
 | |
|     }
 | |
|   } else {
 | |
|     // If there is no active IMEContentObserver, it means that focused content
 | |
|     // may be in another process.
 | |
| 
 | |
|     // If focus is moving from current focused remote process to different
 | |
|     // process while the process has IME focus too, we need to notify IME of
 | |
|     // blur here because it may be too late the blur notification to reach
 | |
|     // this process especially when closing active window.
 | |
|     // However, don't send blur if we're being deactivated and IME wants to
 | |
|     // keep composition during deactive because notifying blur will commit
 | |
|     // or cancel composition.
 | |
|     if (sFocusedIMETabParent && sFocusedIMEWidget &&
 | |
|         (aPresContext ||
 | |
|          !sFocusedIMEWidget->IMENotificationRequestsRef().
 | |
|            WantDuringDeactive()) &&
 | |
|         !IsSameProcess(sFocusedIMETabParent, newTabParent)) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|         ("  OnChangeFocusInternal(), notifying IME of blur of previous focused "
 | |
|          "remote process because it may be too late actual notification to "
 | |
|          "reach this process"));
 | |
|       NotifyIMEOfBlurForChildProcess();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!aPresContext) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnChangeFocusInternal(), "
 | |
|        "no nsPresContext is being activated"));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (sActiveTabParent && !IsSameProcess(sActiveTabParent, newTabParent)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnChangeFocusInternal(), notifying previous "
 | |
|        "focused child process of parent process or another child process "
 | |
|        "getting focus"));
 | |
|     Unused << sActiveTabParent->SendStopIMEStateManagement();
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!newWidget)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  OnChangeFocusInternal(), FAILED due to "
 | |
|        "no widget to manage its IME state"));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Update the cached widget since root view of the presContext may be
 | |
|   // changed to different view.
 | |
|   sWidget = newWidget;
 | |
| 
 | |
|   // If a child process has focus, we should disable IME state until the child
 | |
|   // process actually gets focus because if user types keys before that they
 | |
|   // are handled by IME.
 | |
|   IMEState newState =
 | |
|     newTabParent ? IMEState(IMEState::DISABLED) :
 | |
|                    GetNewIMEState(aPresContext, aContent);
 | |
|   bool setIMEState = true;
 | |
| 
 | |
|   if (newTabParent) {
 | |
|     MOZ_ASSERT(XRE_IsParentProcess());
 | |
|     if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS) {
 | |
|       // If menu keyboard listener is installed, we need to disable IME now.
 | |
|       setIMEState = true;
 | |
|     } else if (aAction.mFocusChange ==
 | |
|                  InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
 | |
|       // If menu keyboard listener is uninstalled, we need to restore
 | |
|       // input context which was set by the remote process.  However, if
 | |
|       // the remote process hasn't been set input context yet, we need to
 | |
|       // wait next SetInputContextForChildProcess() call.
 | |
|       if (HasActiveChildSetInputContext()) {
 | |
|         setIMEState = true;
 | |
|         newState = sActiveChildInputContext.mIMEState;
 | |
|       } else {
 | |
|         setIMEState = false;
 | |
|       }
 | |
|     } else if (focusActuallyChanging) {
 | |
|       InputContext context = newWidget->GetInputContext();
 | |
|       if (context.mIMEState.mEnabled == IMEState::DISABLED &&
 | |
|           context.mOrigin == InputContext::ORIGIN_CONTENT) {
 | |
|         setIMEState = false;
 | |
|         MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|           ("  OnChangeFocusInternal(), doesn't set IME "
 | |
|            "state because focused element (or document) is in a child process "
 | |
|            "and the IME state is already disabled by a remote process"));
 | |
|       } else {
 | |
|         // When new remote process gets focus, we should forget input context
 | |
|         // coming from old focused remote process.
 | |
|         ResetActiveChildInputContext();
 | |
|         MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|           ("  OnChangeFocusInternal(), will disable IME "
 | |
|            "until new focused element (or document) in the child process "
 | |
|            "will get focus actually"));
 | |
|       }
 | |
|     } else if (newWidget->GetInputContext().mOrigin !=
 | |
|                  InputContext::ORIGIN_MAIN) {
 | |
|       // When focus is NOT changed actually, we shouldn't set IME state if
 | |
|       // current input context was set by a remote process since that means
 | |
|       // that the window is being activated and the child process may have
 | |
|       // composition.  Then, we shouldn't commit the composition with making
 | |
|       // IME state disabled.
 | |
|       setIMEState = false;
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  OnChangeFocusInternal(), doesn't set IME "
 | |
|          "state because focused element (or document) is already in the child "
 | |
|          "process"));
 | |
|     }
 | |
|   } else {
 | |
|     // When this process gets focus, we should forget input context coming
 | |
|     // from remote process.
 | |
|     ResetActiveChildInputContext();
 | |
|   }
 | |
| 
 | |
|   if (setIMEState) {
 | |
|     if (!focusActuallyChanging) {
 | |
|       // actual focus isn't changing, but if IME enabled state is changing,
 | |
|       // we should do it.
 | |
|       InputContext context = newWidget->GetInputContext();
 | |
|       if (context.mIMEState.mEnabled == newState.mEnabled) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|           ("  OnChangeFocusInternal(), "
 | |
|            "neither focus nor IME state is changing"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
 | |
| 
 | |
|       // Even if focus isn't changing actually, we should commit current
 | |
|       // composition here since the IME state is changing.
 | |
|       if (sPresContext && oldWidget && !focusActuallyChanging) {
 | |
|         NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
 | |
|                   sFocusedIMETabParent);
 | |
|       }
 | |
|     } else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
 | |
|       // If aContent isn't null or aContent is null but editable, somebody gets
 | |
|       // focus.
 | |
|       bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
 | |
|       aAction.mFocusChange =
 | |
|         gotFocus ? InputContextAction::GOT_FOCUS :
 | |
|                    InputContextAction::LOST_FOCUS;
 | |
|     }
 | |
| 
 | |
|     if (newTabParent && HasActiveChildSetInputContext() &&
 | |
|         aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
 | |
|       // Restore the input context in the active remote process when
 | |
|       // menu keyboard listener is uninstalled and active remote tab has
 | |
|       // focus.
 | |
|       SetInputContext(newWidget, sActiveChildInputContext, aAction);
 | |
|     } else {
 | |
|       // Update IME state for new focus widget
 | |
|       SetIMEState(newState, aPresContext, aContent, newWidget, aAction,
 | |
|                   newTabParent ? InputContext::ORIGIN_CONTENT : sOrigin);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   sActiveTabParent = newTabParent;
 | |
|   sPresContext = aPresContext;
 | |
|   sContent = aContent;
 | |
| 
 | |
|   // Don't call CreateIMEContentObserver() here except when a plugin gets
 | |
|   // focus because it will be called from the focus event handler of focused
 | |
|   // editor.
 | |
|   if (newState.mEnabled == IMEState::PLUGIN) {
 | |
|     CreateIMEContentObserver(nullptr);
 | |
|     if (sActiveIMEContentObserver) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  OnChangeFocusInternal(), an "
 | |
|          "IMEContentObserver instance is created for plugin and trying to "
 | |
|          "flush its pending notifications..."));
 | |
|       sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnInstalledMenuKeyboardListener(bool aInstalling)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnInstalledMenuKeyboardListener(aInstalling=%s), "
 | |
|      "sInstalledMenuKeyboardListener=%s, sActiveTabParent=0x%p, "
 | |
|      "sActiveChildInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, "
 | |
|      "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\", "
 | |
|      "mInPrivateBrowsing=%s }",
 | |
|      GetBoolName(aInstalling),
 | |
|      GetBoolName(sInstalledMenuKeyboardListener),
 | |
|      sActiveTabParent.get(),
 | |
|      GetIMEStateEnabledName(sActiveChildInputContext.mIMEState.mEnabled),
 | |
|      GetIMEStateSetOpenName(sActiveChildInputContext.mIMEState.mOpen),
 | |
|      NS_ConvertUTF16toUTF8(sActiveChildInputContext.mHTMLInputType).get(),
 | |
|      NS_ConvertUTF16toUTF8(sActiveChildInputContext.mHTMLInputInputmode).get(),
 | |
|      NS_ConvertUTF16toUTF8(sActiveChildInputContext.mActionHint).get(),
 | |
|      GetBoolName(sActiveChildInputContext.mInPrivateBrowsing)));
 | |
| 
 | |
|   sInstalledMenuKeyboardListener = aInstalling;
 | |
| 
 | |
|   InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
 | |
|     aInstalling ? InputContextAction::MENU_GOT_PSEUDO_FOCUS :
 | |
|                   InputContextAction::MENU_LOST_PSEUDO_FOCUS);
 | |
|   OnChangeFocusInternal(sPresContext, sContent, action);
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| IMEStateManager::OnMouseButtonEventInEditor(nsPresContext* aPresContext,
 | |
|                                             nsIContent* aContent,
 | |
|                                             WidgetMouseEvent* aMouseEvent)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnMouseButtonEventInEditor(aPresContext=0x%p, "
 | |
|      "aContent=0x%p, aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
 | |
|      aPresContext, aContent, aMouseEvent, sPresContext.get(), sContent.get()));
 | |
| 
 | |
|   if (NS_WARN_IF(!aMouseEvent)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnMouseButtonEventInEditor(), aMouseEvent is nullptr"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (sPresContext != aPresContext || sContent != aContent) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnMouseButtonEventInEditor(), "
 | |
|        "the mouse event isn't fired on the editor managed by ISM"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!sActiveIMEContentObserver) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnMouseButtonEventInEditor(), "
 | |
|        "there is no active IMEContentObserver"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnMouseButtonEventInEditor(), "
 | |
|        "the active IMEContentObserver isn't managing the editor"));
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   bool consumed =
 | |
|     sActiveIMEContentObserver->OnMouseButtonEvent(aPresContext, aMouseEvent);
 | |
| 
 | |
|   if (MOZ_LOG_TEST(sISMLog, LogLevel::Info)) {
 | |
|     nsAutoString eventType;
 | |
|     MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|       ("  OnMouseButtonEventInEditor(), "
 | |
|        "mouse event (mMessage=%s, button=%d) is %s",
 | |
|        ToChar(aMouseEvent->mMessage), aMouseEvent->button,
 | |
|        consumed ? "consumed" : "not consumed"));
 | |
|   }
 | |
| 
 | |
|   return consumed;
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
 | |
|                                  nsIContent* aContent,
 | |
|                                  const WidgetMouseEvent* aMouseEvent)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnClickInEditor(aPresContext=0x%p, aContent=0x%p, aMouseEvent=0x%p), "
 | |
|      "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s)",
 | |
|      aPresContext, aContent, aMouseEvent, sPresContext.get(), sContent.get(),
 | |
|      sWidget, GetBoolName(sWidget && !sWidget->Destroyed())));
 | |
| 
 | |
|   if (NS_WARN_IF(!aMouseEvent)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (sPresContext != aPresContext || sContent != aContent ||
 | |
|       NS_WARN_IF(!sPresContext) || NS_WARN_IF(!sWidget) ||
 | |
|       NS_WARN_IF(sWidget->Destroyed())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnClickInEditor(), "
 | |
|        "the mouse event isn't fired on the editor managed by ISM"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> widget(sWidget);
 | |
| 
 | |
|   MOZ_ASSERT(!sPresContext->GetRootWidget() ||
 | |
|              sPresContext->GetRootWidget() == widget);
 | |
| 
 | |
|   if (!aMouseEvent->IsTrusted()) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnClickInEditor(), "
 | |
|        "the mouse event isn't a trusted event"));
 | |
|     return; // ignore untrusted event.
 | |
|   }
 | |
| 
 | |
|   if (aMouseEvent->button) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnClickInEditor(), "
 | |
|        "the mouse event isn't a left mouse button event"));
 | |
|     return; // not a left click event.
 | |
|   }
 | |
| 
 | |
|   if (aMouseEvent->mClickCount != 1) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnClickInEditor(), "
 | |
|        "the mouse event isn't a single click event"));
 | |
|     return; // should notify only first click event.
 | |
|   }
 | |
| 
 | |
|   InputContextAction::Cause cause =
 | |
|     aMouseEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH ?
 | |
|       InputContextAction::CAUSE_TOUCH : InputContextAction::CAUSE_MOUSE;
 | |
| 
 | |
|   InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED);
 | |
|   IMEState newState = GetNewIMEState(aPresContext, aContent);
 | |
|   SetIMEState(newState, aPresContext, aContent, widget, action, sOrigin);
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
 | |
|                                  nsIContent* aContent,
 | |
|                                  EditorBase& aEditorBase)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnFocusInEditor(aPresContext=0x%p, aContent=0x%p, aEditorBase=0x%p), "
 | |
|      "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p",
 | |
|      aPresContext, aContent, &aEditorBase, sPresContext.get(), sContent.get(),
 | |
|      sActiveIMEContentObserver.get()));
 | |
| 
 | |
|   if (sPresContext != aPresContext || sContent != aContent) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnFocusInEditor(), "
 | |
|        "an editor not managed by ISM gets focus"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If the IMEContentObserver instance isn't managing the editor actually,
 | |
|   // we need to recreate the instance.
 | |
|   if (sActiveIMEContentObserver) {
 | |
|     if (sActiveIMEContentObserver->IsManaging(aPresContext, aContent)) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  OnFocusInEditor(), "
 | |
|          "the editor is already being managed by sActiveIMEContentObserver"));
 | |
|       return;
 | |
|     }
 | |
|     DestroyIMEContentObserver();
 | |
|   }
 | |
| 
 | |
|   CreateIMEContentObserver(&aEditorBase);
 | |
| 
 | |
|   // Let's flush the focus notification now.
 | |
|   if (sActiveIMEContentObserver) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  OnFocusInEditor(), new IMEContentObserver is "
 | |
|        "created, trying to flush pending notifications..."));
 | |
|     sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase)
 | |
| {
 | |
|   if (!sActiveIMEContentObserver ||
 | |
|       !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnEditorInitialized(aEditorBase=0x%p)",
 | |
|      &aEditorBase));
 | |
| 
 | |
|   sActiveIMEContentObserver->UnsuppressNotifyingIME();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnEditorDestroying(EditorBase& aEditorBase)
 | |
| {
 | |
|   if (!sActiveIMEContentObserver ||
 | |
|       !sActiveIMEContentObserver->WasInitializedWith(aEditorBase)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnEditorDestroying(aEditorBase=0x%p)",
 | |
|      &aEditorBase));
 | |
| 
 | |
|   // The IMEContentObserver shouldn't notify IME of anything until reframing
 | |
|   // is finished.
 | |
|   sActiveIMEContentObserver->SuppressNotifyingIME();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
 | |
|                                 nsIContent* aContent,
 | |
|                                 EditorBase* aEditorBase)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("UpdateIMEState(aNewIMEState={ mEnabled=%s, "
 | |
|      "mOpen=%s }, aContent=0x%p, aEditorBase=0x%p), "
 | |
|      "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
 | |
|      "sActiveIMEContentObserver=0x%p, sIsGettingNewIMEState=%s",
 | |
|      GetIMEStateEnabledName(aNewIMEState.mEnabled),
 | |
|      GetIMEStateSetOpenName(aNewIMEState.mOpen), aContent, aEditorBase,
 | |
|      sPresContext.get(), sContent.get(),
 | |
|      sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
 | |
|      sActiveIMEContentObserver.get(),
 | |
|      GetBoolName(sIsGettingNewIMEState)));
 | |
| 
 | |
|   if (sIsGettingNewIMEState) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  UpdateIMEState(), "
 | |
|        "does nothing because of called while getting new IME state"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPresShell> presShell;
 | |
|   if (!aEditorBase) {
 | |
|     MOZ_ASSERT(aContent, "we must have content");
 | |
|     nsIDocument* doc = aContent->OwnerDoc();
 | |
|     presShell = doc->GetShell();
 | |
|   } else {
 | |
|     presShell = aEditorBase->GetPresShell();
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!presShell)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  UpdateIMEState(), FAILED due to "
 | |
|        "editor doesn't have PresShell"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsPresContext* presContext = presShell->GetPresContext();
 | |
|   if (NS_WARN_IF(!presContext)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  UpdateIMEState(), FAILED due to "
 | |
|        "editor doesn't have PresContext"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // IMEStateManager::UpdateIMEState() should be called after
 | |
|   // IMEStateManager::OnChangeFocus() is called for setting focus to aContent
 | |
|   // and aEditorBase.  However, when aEditorBase is an HTMLEditor, this may be
 | |
|   // called by nsIEditor::PostCreate() before IMEStateManager::OnChangeFocus().
 | |
|   // Similarly, when aEditorBase is a TextEditor, this may be called by
 | |
|   // nsIEditor::SetFlags().  In such cases, this method should do nothing
 | |
|   // because input context should be updated when
 | |
|   // IMEStateManager::OnChangeFocus() is called later.
 | |
|   if (sPresContext != presContext) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|       ("  UpdateIMEState(), does nothing due to "
 | |
|        "the editor hasn't managed by IMEStateManager yet"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If IMEStateManager doesn't manage any document, this cannot update IME
 | |
|   // state of any widget.
 | |
|   if (NS_WARN_IF(!sPresContext)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  UpdateIMEState(), FAILED due to "
 | |
|        "no managing nsPresContext"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  UpdateIMEState(), FAILED due to "
 | |
|        "the widget for the managing nsPresContext has gone"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> widget(sWidget);
 | |
| 
 | |
|   MOZ_ASSERT(!sPresContext->GetRootWidget() ||
 | |
|              sPresContext->GetRootWidget() == widget);
 | |
| 
 | |
|   // Even if there is active IMEContentObserver, it may not be observing the
 | |
|   // editor with current editable root content due to reframed.  In such case,
 | |
|   // We should try to reinitialize the IMEContentObserver.
 | |
|   if (sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  UpdateIMEState(), try to reinitialize the "
 | |
|        "active IMEContentObserver"));
 | |
|     RefPtr<IMEContentObserver> contentObserver = sActiveIMEContentObserver;
 | |
|     if (!contentObserver->MaybeReinitialize(widget, sPresContext,
 | |
|                                             aContent, aEditorBase)) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|         ("  UpdateIMEState(), failed to reinitialize the "
 | |
|          "active IMEContentObserver"));
 | |
|     }
 | |
|     if (NS_WARN_IF(widget->Destroyed())) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|         ("  UpdateIMEState(), widget has gone during reinitializing the "
 | |
|          "active IMEContentObserver"));
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If there is no active IMEContentObserver or it isn't observing the
 | |
|   // editor correctly, we should recreate it.
 | |
|   bool createTextStateManager =
 | |
|     (!sActiveIMEContentObserver ||
 | |
|      !sActiveIMEContentObserver->IsManaging(sPresContext, aContent));
 | |
| 
 | |
|   bool updateIMEState =
 | |
|     (widget->GetInputContext().mIMEState.mEnabled != aNewIMEState.mEnabled);
 | |
|   if (NS_WARN_IF(widget->Destroyed())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  UpdateIMEState(), widget has gone during getting input context"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (updateIMEState) {
 | |
|     // commit current composition before modifying IME state.
 | |
|     NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, widget, sFocusedIMETabParent);
 | |
|     if (NS_WARN_IF(widget->Destroyed())) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|         ("  UpdateIMEState(), widget has gone during committing composition"));
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (createTextStateManager) {
 | |
|     DestroyIMEContentObserver();
 | |
|   }
 | |
| 
 | |
|   if (updateIMEState) {
 | |
|     InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
 | |
|                               InputContextAction::FOCUS_NOT_CHANGED);
 | |
|     SetIMEState(aNewIMEState, presContext, aContent, widget, action, sOrigin);
 | |
|     if (NS_WARN_IF(widget->Destroyed())) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|         ("  UpdateIMEState(), widget has gone during setting input context"));
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (createTextStateManager) {
 | |
|     // XXX In this case, it might not be enough safe to notify IME of anything.
 | |
|     //     So, don't try to flush pending notifications of IMEContentObserver
 | |
|     //     here.
 | |
|     CreateIMEContentObserver(aEditorBase);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| IMEState
 | |
| IMEStateManager::GetNewIMEState(nsPresContext* aPresContext,
 | |
|                                 nsIContent*    aContent)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("GetNewIMEState(aPresContext=0x%p, aContent=0x%p), "
 | |
|      "sInstalledMenuKeyboardListener=%s",
 | |
|      aPresContext, aContent, GetBoolName(sInstalledMenuKeyboardListener)));
 | |
| 
 | |
|   if (!CanHandleWith(aPresContext)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  GetNewIMEState() returns DISABLED because "
 | |
|        "the nsPresContext has been destroyed"));
 | |
|     return IMEState(IMEState::DISABLED);
 | |
|   }
 | |
| 
 | |
|   // On Printing or Print Preview, we don't need IME.
 | |
|   if (aPresContext->Type() == nsPresContext::eContext_PrintPreview ||
 | |
|       aPresContext->Type() == nsPresContext::eContext_Print) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  GetNewIMEState() returns DISABLED because "
 | |
|        "the nsPresContext is for print or print preview"));
 | |
|     return IMEState(IMEState::DISABLED);
 | |
|   }
 | |
| 
 | |
|   if (sInstalledMenuKeyboardListener) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  GetNewIMEState() returns DISABLED because "
 | |
|        "menu keyboard listener was installed"));
 | |
|     return IMEState(IMEState::DISABLED);
 | |
|   }
 | |
| 
 | |
|   if (!aContent) {
 | |
|     // Even if there are no focused content, the focused document might be
 | |
|     // editable, such case is design mode.
 | |
|     nsIDocument* doc = aPresContext->Document();
 | |
|     if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  GetNewIMEState() returns ENABLED because "
 | |
|          "design mode editor has focus"));
 | |
|       return IMEState(IMEState::ENABLED);
 | |
|     }
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  GetNewIMEState() returns DISABLED because "
 | |
|        "no content has focus"));
 | |
|     return IMEState(IMEState::DISABLED);
 | |
|   }
 | |
| 
 | |
|   // nsIContent::GetDesiredIMEState() may cause a call of UpdateIMEState()
 | |
|   // from EditorBase::PostCreate() because GetDesiredIMEState() needs to
 | |
|   // retrieve an editor instance for the element if it's editable element.
 | |
|   // For avoiding such nested IME state updates, we should set
 | |
|   // sIsGettingNewIMEState here and UpdateIMEState() should check it.
 | |
|   GettingNewIMEStateBlocker blocker;
 | |
| 
 | |
|   IMEState newIMEState = aContent->GetDesiredIMEState();
 | |
|   MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|     ("  GetNewIMEState() returns { mEnabled=%s, "
 | |
|      "mOpen=%s }",
 | |
|      GetIMEStateEnabledName(newIMEState.mEnabled),
 | |
|      GetIMEStateSetOpenName(newIMEState.mOpen)));
 | |
|   return newIMEState;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| MayBeIMEUnawareWebApp(nsINode* aNode)
 | |
| {
 | |
|   bool haveKeyEventsListener = false;
 | |
| 
 | |
|   while (aNode) {
 | |
|     EventListenerManager* const mgr = aNode->GetExistingListenerManager();
 | |
|     if (mgr) {
 | |
|       if (mgr->MayHaveInputOrCompositionEventListener()) {
 | |
|         return false;
 | |
|       }
 | |
|       haveKeyEventsListener |= mgr->MayHaveKeyEventListener();
 | |
|     }
 | |
|     aNode = aNode->GetParentNode();
 | |
|   }
 | |
| 
 | |
|   return haveKeyEventsListener;
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::ResetActiveChildInputContext()
 | |
| {
 | |
|   sActiveChildInputContext.mIMEState.mEnabled = IMEState::UNKNOWN;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| IMEStateManager::HasActiveChildSetInputContext()
 | |
| {
 | |
|   return sActiveChildInputContext.mIMEState.mEnabled != IMEState::UNKNOWN;
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::SetInputContextForChildProcess(
 | |
|                    TabParent* aTabParent,
 | |
|                    const InputContext& aInputContext,
 | |
|                    const InputContextAction& aAction)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("SetInputContextForChildProcess(aTabParent=0x%p, "
 | |
|      "aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, "
 | |
|      "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\", "
 | |
|      "mInPrivateBrowsing=%s }, aAction={ mCause=%s, mAction=%s }), "
 | |
|      "sPresContext=0x%p (available: %s), sWidget=0x%p (available: %s), "
 | |
|      "sActiveTabParent=0x%p, sInstalledMenuKeyboardListener=%s",
 | |
|      aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
 | |
|      GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
 | |
|      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
 | |
|      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
 | |
|      NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
 | |
|      GetBoolName(aInputContext.mInPrivateBrowsing),
 | |
|      GetActionCauseName(aAction.mCause),
 | |
|      GetActionFocusChangeName(aAction.mFocusChange),
 | |
|      sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)),
 | |
|      sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
 | |
|      sActiveTabParent.get(), GetBoolName(sInstalledMenuKeyboardListener)));
 | |
| 
 | |
|   if (aTabParent != sActiveTabParent) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  SetInputContextForChildProcess(), FAILED, "
 | |
|        "because non-focused tab parent tries to set input context"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!CanHandleWith(sPresContext))) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  SetInputContextForChildProcess(), FAILED, "
 | |
|        "due to no focused presContext"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!sWidget) || NS_WARN_IF(sWidget->Destroyed())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  SetInputContextForChildProcess(), FAILED, "
 | |
|        "due to the widget for the nsPresContext has gone"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> widget(sWidget);
 | |
| 
 | |
|   MOZ_ASSERT(!sPresContext->GetRootWidget() ||
 | |
|              sPresContext->GetRootWidget() == widget);
 | |
|   MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
 | |
| 
 | |
|   sActiveChildInputContext = aInputContext;
 | |
|   MOZ_ASSERT(HasActiveChildSetInputContext());
 | |
| 
 | |
|   // If input context is changed in remote process while menu keyboard listener
 | |
|   // is installed, this process shouldn't set input context now.  When it's
 | |
|   // uninstalled, input context should be restored from
 | |
|   // sActiveChildInputContext.
 | |
|   if (sInstalledMenuKeyboardListener) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|       ("  SetInputContextForChildProcess(), waiting to set input context "
 | |
|        "until menu keyboard listener is uninstalled"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   SetInputContext(widget, aInputContext, aAction);
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::SetIMEState(const IMEState& aState,
 | |
|                              nsPresContext* aPresContext,
 | |
|                              nsIContent* aContent,
 | |
|                              nsIWidget* aWidget,
 | |
|                              InputContextAction aAction,
 | |
|                              InputContext::Origin aOrigin)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("SetIMEState(aState={ mEnabled=%s, mOpen=%s }, "
 | |
|      "aContent=0x%p (TabParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, "
 | |
|      "mFocusChange=%s }, aOrigin=%s)",
 | |
|      GetIMEStateEnabledName(aState.mEnabled),
 | |
|      GetIMEStateSetOpenName(aState.mOpen), aContent,
 | |
|      TabParent::GetFrom(aContent), aWidget,
 | |
|      GetActionCauseName(aAction.mCause),
 | |
|      GetActionFocusChangeName(aAction.mFocusChange),
 | |
|      ToChar(aOrigin)));
 | |
| 
 | |
|   NS_ENSURE_TRUE_VOID(aWidget);
 | |
| 
 | |
|   InputContext context;
 | |
|   context.mIMEState = aState;
 | |
|   context.mOrigin = aOrigin;
 | |
|   context.mMayBeIMEUnaware = context.mIMEState.IsEditable() &&
 | |
|     sCheckForIMEUnawareWebApps && MayBeIMEUnawareWebApp(aContent);
 | |
| 
 | |
|   context.mHasHandledUserInput =
 | |
|     aPresContext && aPresContext->PresShell()->HasHandledUserInput();
 | |
| 
 | |
|   context.mInPrivateBrowsing =
 | |
|     aPresContext &&
 | |
|     nsContentUtils::IsInPrivateBrowsing(aPresContext->Document());
 | |
| 
 | |
|   if (aContent &&
 | |
|       aContent->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) {
 | |
|     if (!aContent->IsHTMLElement(nsGkAtoms::textarea)) {
 | |
|       // <input type=number> has an anonymous <input type=text> descendant
 | |
|       // that gets focus whenever anyone tries to focus the number control. We
 | |
|       // need to check if aContent is one of those anonymous text controls and,
 | |
|       // if so, use the number control instead:
 | |
|       Element* element = aContent->AsElement();
 | |
|       HTMLInputElement* inputElement =
 | |
|         HTMLInputElement::FromContentOrNull(aContent);
 | |
|       if (inputElement) {
 | |
|         HTMLInputElement* ownerNumberControl =
 | |
|           inputElement->GetOwnerNumberControl();
 | |
|         if (ownerNumberControl) {
 | |
|           element = ownerNumberControl; // an <input type=number>
 | |
|         }
 | |
|       }
 | |
|       element->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
 | |
|                        context.mHTMLInputType);
 | |
|     } else {
 | |
|       context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
 | |
|     }
 | |
| 
 | |
|     if (sInputModeSupported ||
 | |
|         nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
 | |
|       aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
 | |
|                                      context.mHTMLInputInputmode);
 | |
|       if (context.mHTMLInputInputmode.EqualsLiteral("mozAwesomebar") &&
 | |
|           !nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
 | |
|         // mozAwesomebar should be allowed only in chrome
 | |
|         context.mHTMLInputInputmode.Truncate();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     aContent->AsElement()->GetAttr(kNameSpaceID_None,
 | |
|                                    nsGkAtoms::moz_action_hint,
 | |
|                                    context.mActionHint);
 | |
| 
 | |
|     // Get the input content corresponding to the focused node,
 | |
|     // which may be an anonymous child of the input content.
 | |
|     nsIContent* inputContent = aContent->FindFirstNonChromeOnlyAccessContent();
 | |
| 
 | |
|     // If we don't have an action hint and
 | |
|     // return won't submit the form, use "next".
 | |
|     if (context.mActionHint.IsEmpty() &&
 | |
|         inputContent->IsHTMLElement(nsGkAtoms::input)) {
 | |
|       bool willSubmit = false;
 | |
|       nsCOMPtr<nsIFormControl> control(do_QueryInterface(inputContent));
 | |
|       mozilla::dom::Element* formElement = nullptr;
 | |
|       nsCOMPtr<nsIForm> form;
 | |
|       if (control) {
 | |
|         formElement = control->GetFormElement();
 | |
|         // is this a form and does it have a default submit element?
 | |
|         if ((form = do_QueryInterface(formElement)) &&
 | |
|             form->GetDefaultSubmitElement()) {
 | |
|           willSubmit = true;
 | |
|         // is this an html form...
 | |
|         } else if (formElement && formElement->IsHTMLElement(nsGkAtoms::form)) {
 | |
|           dom::HTMLFormElement* htmlForm =
 | |
|             static_cast<dom::HTMLFormElement*>(formElement);
 | |
|           // ... and does it only have a single text input element ?
 | |
|           if (!htmlForm->ImplicitSubmissionIsDisabled() ||
 | |
|               // ... or is this the last non-disabled element?
 | |
|               htmlForm->IsLastActiveElement(control)) {
 | |
|             willSubmit = true;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       context.mActionHint.Assign(
 | |
|         willSubmit ? (control->ControlType() == NS_FORM_INPUT_SEARCH ?
 | |
|                        NS_LITERAL_STRING("search") : NS_LITERAL_STRING("go")) :
 | |
|                      (formElement ?
 | |
|                        NS_LITERAL_STRING("next") : EmptyString()));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // XXX I think that we should use nsContentUtils::IsCallerChrome() instead
 | |
|   //     of the process type.
 | |
|   if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN &&
 | |
|       !XRE_IsContentProcess()) {
 | |
|     aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
 | |
|   }
 | |
| 
 | |
|   if ((aAction.mCause == InputContextAction::CAUSE_UNKNOWN ||
 | |
|        aAction.mCause == InputContextAction::CAUSE_UNKNOWN_CHROME) &&
 | |
|       EventStateManager::IsHandlingUserInput()) {
 | |
|     aAction.mCause = EventStateManager::IsHandlingKeyboardInput() ?
 | |
|       InputContextAction::CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT :
 | |
|       InputContextAction::CAUSE_UNKNOWN_DURING_NON_KEYBOARD_INPUT;
 | |
|   }
 | |
| 
 | |
|   SetInputContext(aWidget, context, aAction);
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::SetInputContext(nsIWidget* aWidget,
 | |
|                                  const InputContext& aInputContext,
 | |
|                                  const InputContextAction& aAction)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("SetInputContext(aWidget=0x%p, aInputContext={ "
 | |
|      "mIMEState={ mEnabled=%s, mOpen=%s }, mHTMLInputType=\"%s\", "
 | |
|      "mHTMLInputInputmode=\"%s\", mActionHint=\"%s\", "
 | |
|      "mInPrivateBrowsing=%s }, "
 | |
|      "aAction={ mCause=%s, mAction=%s }), sActiveTabParent=0x%p",
 | |
|      aWidget,
 | |
|      GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
 | |
|      GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
 | |
|      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
 | |
|      NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
 | |
|      NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
 | |
|      GetBoolName(aInputContext.mInPrivateBrowsing),
 | |
|      GetActionCauseName(aAction.mCause),
 | |
|      GetActionFocusChangeName(aAction.mFocusChange),
 | |
|      sActiveTabParent.get()));
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(aWidget);
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> widget(aWidget);
 | |
|   widget->SetInputContext(aInputContext, aAction);
 | |
|   sActiveInputContextWidget = widget;
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::EnsureTextCompositionArray()
 | |
| {
 | |
|   if (sTextCompositions) {
 | |
|     return;
 | |
|   }
 | |
|   sTextCompositions = new TextCompositionArray();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::DispatchCompositionEvent(
 | |
|                    nsINode* aEventTargetNode,
 | |
|                    nsPresContext* aPresContext,
 | |
|                    WidgetCompositionEvent* aCompositionEvent,
 | |
|                    nsEventStatus* aStatus,
 | |
|                    EventDispatchingCallback* aCallBack,
 | |
|                    bool aIsSynthesized)
 | |
| {
 | |
|   RefPtr<TabParent> tabParent =
 | |
|     aEventTargetNode->IsContent() ?
 | |
|       TabParent::GetFrom(aEventTargetNode->AsContent()) : nullptr;
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("DispatchCompositionEvent(aNode=0x%p, "
 | |
|      "aPresContext=0x%p, aCompositionEvent={ mMessage=%s, "
 | |
|      "mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
 | |
|      "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
 | |
|      "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
 | |
|      "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
 | |
|      "mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
 | |
|      "aIsSynthesized=%s), tabParent=%p",
 | |
|      aEventTargetNode, aPresContext,
 | |
|      ToChar(aCompositionEvent->mMessage),
 | |
|      aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
 | |
|      aCompositionEvent->mNativeIMEContext.mOriginProcessID,
 | |
|      aCompositionEvent->mWidget.get(),
 | |
|      aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
 | |
|      aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
 | |
|      GetBoolName(aCompositionEvent->mWidget->Destroyed()),
 | |
|      GetBoolName(aCompositionEvent->mFlags.mIsTrusted),
 | |
|      GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
 | |
|      GetBoolName(aIsSynthesized), tabParent.get()));
 | |
| 
 | |
|   if (!aCompositionEvent->IsTrusted() ||
 | |
|       aCompositionEvent->PropagationStopped()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
 | |
|              "compositionupdate event shouldn't be dispatched manually");
 | |
| 
 | |
|   EnsureTextCompositionArray();
 | |
| 
 | |
|   RefPtr<TextComposition> composition =
 | |
|     sTextCompositions->GetCompositionFor(aCompositionEvent);
 | |
|   if (!composition) {
 | |
|     // If synthesized event comes after delayed native composition events
 | |
|     // for request of commit or cancel, we should ignore it.
 | |
|     if (NS_WARN_IF(aIsSynthesized)) {
 | |
|       return;
 | |
|     }
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  DispatchCompositionEvent(), "
 | |
|        "adding new TextComposition to the array"));
 | |
|     MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
 | |
|     composition =
 | |
|       new TextComposition(aPresContext, aEventTargetNode, tabParent,
 | |
|                           aCompositionEvent);
 | |
|     sTextCompositions->AppendElement(composition);
 | |
|   }
 | |
| #ifdef DEBUG
 | |
|   else {
 | |
|     MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
 | |
|   }
 | |
| #endif // #ifdef DEBUG
 | |
| 
 | |
|   // Dispatch the event on composing target.
 | |
|   composition->DispatchCompositionEvent(aCompositionEvent, aStatus, aCallBack,
 | |
|                                         aIsSynthesized);
 | |
| 
 | |
|   // WARNING: the |composition| might have been destroyed already.
 | |
| 
 | |
|   // Remove the ended composition from the array.
 | |
|   // NOTE: When TextComposition is synthesizing compositionend event for
 | |
|   //       emulating a commit, the instance shouldn't be removed from the array
 | |
|   //       because IME may perform it later.  Then, we need to ignore the
 | |
|   //       following commit events in TextComposition::DispatchEvent().
 | |
|   //       However, if commit or cancel for a request is performed synchronously
 | |
|   //       during not safe to dispatch events, PresShell must have discarded
 | |
|   //       compositionend event.  Then, the synthesized compositionend event is
 | |
|   //       the last event for the composition.  In this case, we need to
 | |
|   //       destroy the TextComposition with synthesized compositionend event.
 | |
|   if ((!aIsSynthesized ||
 | |
|        composition->WasNativeCompositionEndEventDiscarded()) &&
 | |
|       aCompositionEvent->CausesDOMCompositionEndEvent()) {
 | |
|     TextCompositionArray::index_type i =
 | |
|       sTextCompositions->IndexOf(aCompositionEvent->mWidget);
 | |
|     if (i != TextCompositionArray::NoIndex) {
 | |
|       MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|         ("  DispatchCompositionEvent(), "
 | |
|          "removing TextComposition from the array since NS_COMPOSTION_END "
 | |
|          "was dispatched"));
 | |
|       sTextCompositions->ElementAt(i)->Destroy();
 | |
|       sTextCompositions->RemoveElementAt(i);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| IMEContentObserver*
 | |
| IMEStateManager::GetActiveContentObserver()
 | |
| {
 | |
|   return sActiveIMEContentObserver;
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsIContent*
 | |
| IMEStateManager::GetRootContent(nsPresContext* aPresContext)
 | |
| {
 | |
|   nsIDocument* doc = aPresContext->Document();
 | |
|   if (NS_WARN_IF(!doc)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return doc->GetRootElement();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::HandleSelectionEvent(nsPresContext* aPresContext,
 | |
|                                       nsIContent* aEventTargetContent,
 | |
|                                       WidgetSelectionEvent* aSelectionEvent)
 | |
| {
 | |
|   nsIContent* eventTargetContent =
 | |
|     aEventTargetContent ? aEventTargetContent :
 | |
|                           GetRootContent(aPresContext);
 | |
|   RefPtr<TabParent> tabParent =
 | |
|     eventTargetContent ? TabParent::GetFrom(eventTargetContent) : nullptr;
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("HandleSelectionEvent(aPresContext=0x%p, "
 | |
|      "aEventTargetContent=0x%p, aSelectionEvent={ mMessage=%s, "
 | |
|      "mFlags={ mIsTrusted=%s } }), tabParent=%p",
 | |
|      aPresContext, aEventTargetContent,
 | |
|      ToChar(aSelectionEvent->mMessage),
 | |
|      GetBoolName(aSelectionEvent->mFlags.mIsTrusted),
 | |
|      tabParent.get()));
 | |
| 
 | |
|   if (!aSelectionEvent->IsTrusted()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<TextComposition> composition = sTextCompositions ?
 | |
|     sTextCompositions->GetCompositionFor(aSelectionEvent->mWidget) : nullptr;
 | |
|   if (composition) {
 | |
|     // When there is a composition, TextComposition should guarantee that the
 | |
|     // selection event will be handled in same target as composition events.
 | |
|     composition->HandleSelectionEvent(aSelectionEvent);
 | |
|   } else {
 | |
|     // When there is no composition, the selection event should be handled
 | |
|     // in the aPresContext or tabParent.
 | |
|     TextComposition::HandleSelectionEvent(aPresContext, tabParent,
 | |
|                                           aSelectionEvent);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::OnCompositionEventDiscarded(
 | |
|                    WidgetCompositionEvent* aCompositionEvent)
 | |
| {
 | |
|   // Note that this method is never called for synthesized events for emulating
 | |
|   // commit or cancel composition.
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("OnCompositionEventDiscarded(aCompositionEvent={ "
 | |
|      "mMessage=%s, mNativeIMEContext={ mRawNativeIMEContext=0x%" PRIXPTR ", "
 | |
|      "mOriginProcessID=0x%" PRIX64 " }, mWidget(0x%p)={ "
 | |
|      "GetNativeIMEContext()={ mRawNativeIMEContext=0x%" PRIXPTR ", "
 | |
|      "mOriginProcessID=0x%" PRIX64 " }, Destroyed()=%s }, "
 | |
|      "mFlags={ mIsTrusted=%s } })",
 | |
|      ToChar(aCompositionEvent->mMessage),
 | |
|      aCompositionEvent->mNativeIMEContext.mRawNativeIMEContext,
 | |
|      aCompositionEvent->mNativeIMEContext.mOriginProcessID,
 | |
|      aCompositionEvent->mWidget.get(),
 | |
|      aCompositionEvent->mWidget->GetNativeIMEContext().mRawNativeIMEContext,
 | |
|      aCompositionEvent->mWidget->GetNativeIMEContext().mOriginProcessID,
 | |
|      GetBoolName(aCompositionEvent->mWidget->Destroyed()),
 | |
|      GetBoolName(aCompositionEvent->mFlags.mIsTrusted)));
 | |
| 
 | |
|   if (!aCompositionEvent->IsTrusted()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Ignore compositionstart for now because sTextCompositions may not have
 | |
|   // been created yet.
 | |
|   if (aCompositionEvent->mMessage == eCompositionStart) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<TextComposition> composition =
 | |
|     sTextCompositions->GetCompositionFor(aCompositionEvent->mWidget);
 | |
|   if (!composition) {
 | |
|     // If the PresShell has been being destroyed during composition,
 | |
|     // a TextComposition instance for the composition was already removed from
 | |
|     // the array and destroyed in OnDestroyPresContext().  Therefore, we may
 | |
|     // fail to retrieve a TextComposition instance here.
 | |
|     MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|       ("  OnCompositionEventDiscarded(), "
 | |
|        "TextComposition instance for the widget has already gone"));
 | |
|     return;
 | |
|   }
 | |
|   composition->OnCompositionEventDiscarded(aCompositionEvent);
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::NotifyIME(IMEMessage aMessage,
 | |
|                            nsIWidget* aWidget,
 | |
|                            TabParent* aTabParent)
 | |
| {
 | |
|   return IMEStateManager::NotifyIME(IMENotification(aMessage), aWidget,
 | |
|                                     aTabParent);
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::NotifyIME(const IMENotification& aNotification,
 | |
|                            nsIWidget* aWidget,
 | |
|                            TabParent* aTabParent)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("NotifyIME(aNotification={ mMessage=%s }, "
 | |
|      "aWidget=0x%p, aTabParent=0x%p), sFocusedIMEWidget=0x%p, "
 | |
|      "sActiveTabParent=0x%p, sFocusedIMETabParent=0x%p, "
 | |
|      "IsSameProcess(aTabParent, sActiveTabParent)=%s, "
 | |
|      "IsSameProcess(aTabParent, sFocusedIMETabParent)=%s",
 | |
|      ToChar(aNotification.mMessage), aWidget,
 | |
|      aTabParent, sFocusedIMEWidget, sActiveTabParent.get(),
 | |
|      sFocusedIMETabParent.get(),
 | |
|      GetBoolName(IsSameProcess(aTabParent, sActiveTabParent)),
 | |
|      GetBoolName(IsSameProcess(aTabParent, sFocusedIMETabParent))));
 | |
| 
 | |
|   if (NS_WARN_IF(!aWidget)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  NotifyIME(), FAILED due to no widget"));
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   }
 | |
| 
 | |
|   switch (aNotification.mMessage) {
 | |
|     case NOTIFY_IME_OF_FOCUS: {
 | |
|       // If focus notification comes from a remote process which already lost
 | |
|       // focus, we shouldn't accept the focus notification.  Then, following
 | |
|       // notifications from the process will be ignored.
 | |
|       if (NS_WARN_IF(!IsSameProcess(aTabParent, sActiveTabParent))) {
 | |
|         MOZ_ASSERT(aTabParent,
 | |
|           "Why was the input context initialized for a remote process but "
 | |
|           "does this process get IME focus?");
 | |
|         MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|           ("  NotifyIME(), WARNING, the received focus notification is ignored "
 | |
|            "because input context was initialized for %s, perhaps, it came "
 | |
|            "from a busy remote process",
 | |
|            sActiveTabParent ? "another remote process" : "current process"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       // If there is pending blur notification for current focused IME,
 | |
|       // we should notify IME of blur by ourselves.  Then, we should ignore
 | |
|       // following notifications coming from the process.
 | |
|       if (sFocusedIMEWidget) {
 | |
|         MOZ_ASSERT(sFocusedIMETabParent || aTabParent,
 | |
|           "This case shouldn't be caused by focus move in this process");
 | |
|         nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
 | |
|         sFocusedIMEWidget = nullptr;
 | |
|         sFocusedIMETabParent = nullptr;
 | |
|         focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
 | |
|       }
 | |
|       sFocusedIMETabParent = aTabParent;
 | |
|       sFocusedIMEWidget = aWidget;
 | |
|       nsCOMPtr<nsIWidget> widget(aWidget);
 | |
|       return widget->NotifyIME(aNotification);
 | |
|     }
 | |
|     case NOTIFY_IME_OF_BLUR: {
 | |
|       if (!IsSameProcess(aTabParent, sFocusedIMETabParent)) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|           ("  NotifyIME(), WARNING, the received blur notification is ignored "
 | |
|            "because it's not from current focused IME process"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       if (!sFocusedIMEWidget) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|           ("  NotifyIME(), WARNING, received blur notification but there is "
 | |
|            "no focused IME widget"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|           ("  NotifyIME(), WARNING, the received blur notification is ignored "
 | |
|            "because it's not for current focused IME widget"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
 | |
|       sFocusedIMEWidget = nullptr;
 | |
|       sFocusedIMETabParent = nullptr;
 | |
|       return focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
 | |
|     }
 | |
|     case NOTIFY_IME_OF_SELECTION_CHANGE:
 | |
|     case NOTIFY_IME_OF_TEXT_CHANGE:
 | |
|     case NOTIFY_IME_OF_POSITION_CHANGE:
 | |
|     case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
 | |
|     case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: {
 | |
|       if (!IsSameProcess(aTabParent, sFocusedIMETabParent)) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|           ("  NotifyIME(), WARNING, the received content change notification "
 | |
|            "is ignored because it's not from current focused IME process"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       if (!sFocusedIMEWidget) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|           ("  NotifyIME(), WARNING, the received content change notification "
 | |
|            "is ignored because there is no focused IME widget"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       if (NS_WARN_IF(sFocusedIMEWidget != aWidget)) {
 | |
|         MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|           ("  NotifyIME(), WARNING, the received content change notification "
 | |
|            "is ignored because it's not for current focused IME widget"));
 | |
|         return NS_OK;
 | |
|       }
 | |
|       nsCOMPtr<nsIWidget> widget(aWidget);
 | |
|       return widget->NotifyIME(aNotification);
 | |
|     }
 | |
|     default:
 | |
|       // Other notifications should be sent only when there is composition.
 | |
|       // So, we need to handle the others below.
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   if (!sTextCompositions) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|       ("  NotifyIME(), the request to IME is ignored because "
 | |
|        "there have been no compositions yet"));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   RefPtr<TextComposition> composition =
 | |
|     sTextCompositions->GetCompositionFor(aWidget);
 | |
|   if (!composition) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|       ("  NotifyIME(), the request to IME is ignored because "
 | |
|        "there is no active composition"));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (!IsSameProcess(aTabParent, composition->GetTabParent())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Warning,
 | |
|       ("  NotifyIME(), WARNING, the request to IME is ignored because "
 | |
|        "it does not come from the remote process which has the composition "
 | |
|        "on aWidget"));
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   switch (aNotification.mMessage) {
 | |
|     case REQUEST_TO_COMMIT_COMPOSITION:
 | |
|       return composition->RequestToCommit(aWidget, false);
 | |
|     case REQUEST_TO_CANCEL_COMPOSITION:
 | |
|       return composition->RequestToCommit(aWidget, true);
 | |
|     default:
 | |
|       MOZ_CRASH("Unsupported notification");
 | |
|   }
 | |
|   MOZ_CRASH(
 | |
|     "Failed to handle the notification for non-synthesized composition");
 | |
|   return NS_ERROR_FAILURE;
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::NotifyIME(IMEMessage aMessage,
 | |
|                            nsPresContext* aPresContext,
 | |
|                            TabParent* aTabParent)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("NotifyIME(aMessage=%s, aPresContext=0x%p, aTabParent=0x%p)",
 | |
|      ToChar(aMessage), aPresContext, aTabParent));
 | |
| 
 | |
|   if (NS_WARN_IF(!CanHandleWith(aPresContext))) {
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   }
 | |
| 
 | |
|   nsIWidget* widget = aPresContext->GetRootWidget();
 | |
|   if (NS_WARN_IF(!widget)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  NotifyIME(), FAILED due to no widget for the "
 | |
|        "nsPresContext"));
 | |
|     return NS_ERROR_NOT_AVAILABLE;
 | |
|   }
 | |
|   return NotifyIME(aMessage, widget, aTabParent);
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| IMEStateManager::IsEditable(nsINode* node)
 | |
| {
 | |
|   if (node->IsEditable()) {
 | |
|     return true;
 | |
|   }
 | |
|   // |node| might be readwrite (for example, a text control)
 | |
|   if (node->IsElement() &&
 | |
|       node->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsINode*
 | |
| IMEStateManager::GetRootEditableNode(nsPresContext* aPresContext,
 | |
|                                      nsIContent* aContent)
 | |
| {
 | |
|   if (aContent) {
 | |
|     nsINode* root = nullptr;
 | |
|     nsINode* node = aContent;
 | |
|     while (node && IsEditable(node)) {
 | |
|       // If the node has independent selection like <input type="text"> or
 | |
|       // <textarea>, the node should be the root editable node for aContent.
 | |
|       // FYI: <select> element also has independent selection but IsEditable()
 | |
|       //      returns false.
 | |
|       // XXX: If somebody adds new editable element which has independent
 | |
|       //      selection but doesn't own editor, we'll need more checks here.
 | |
|       if (node->IsContent() &&
 | |
|           node->AsContent()->HasIndependentSelection()) {
 | |
|         return node;
 | |
|       }
 | |
|       root = node;
 | |
|       node = node->GetParentNode();
 | |
|     }
 | |
|     return root;
 | |
|   }
 | |
|   if (aPresContext) {
 | |
|     nsIDocument* document = aPresContext->Document();
 | |
|     if (document && document->IsEditable()) {
 | |
|       return document;
 | |
|     }
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| IMEStateManager::IsIMEObserverNeeded(const IMEState& aState)
 | |
| {
 | |
|   return aState.MaybeEditable();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::DestroyIMEContentObserver()
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("DestroyIMEContentObserver(), sActiveIMEContentObserver=0x%p",
 | |
|      sActiveIMEContentObserver.get()));
 | |
| 
 | |
|   if (!sActiveIMEContentObserver) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  DestroyIMEContentObserver() does nothing"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|     ("  DestroyIMEContentObserver(), destroying "
 | |
|      "the active IMEContentObserver..."));
 | |
|   RefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
 | |
|   sActiveIMEContentObserver = nullptr;
 | |
|   tsm->Destroy();
 | |
| }
 | |
| 
 | |
| // static
 | |
| void
 | |
| IMEStateManager::CreateIMEContentObserver(EditorBase* aEditorBase)
 | |
| {
 | |
|   MOZ_LOG(sISMLog, LogLevel::Info,
 | |
|     ("CreateIMEContentObserver(aEditorBase=0x%p), "
 | |
|      "sPresContext=0x%p, sContent=0x%p, sWidget=0x%p (available: %s), "
 | |
|      "sActiveIMEContentObserver=0x%p, "
 | |
|      "sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
 | |
|      aEditorBase, sPresContext.get(), sContent.get(),
 | |
|      sWidget, GetBoolName(sWidget && !sWidget->Destroyed()),
 | |
|      sActiveIMEContentObserver.get(),
 | |
|      GetBoolName(sActiveIMEContentObserver ?
 | |
|        sActiveIMEContentObserver->IsManaging(sPresContext, sContent) : false)));
 | |
| 
 | |
|   if (NS_WARN_IF(sActiveIMEContentObserver)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  CreateIMEContentObserver(), FAILED due to "
 | |
|        "there is already an active IMEContentObserver"));
 | |
|     MOZ_ASSERT(sActiveIMEContentObserver->IsManaging(sPresContext, sContent));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!sWidget || NS_WARN_IF(sWidget->Destroyed())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  CreateIMEContentObserver(), FAILED due to "
 | |
|        "the widget for the nsPresContext has gone"));
 | |
|     return; // Sometimes, there are no widgets.
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> widget(sWidget);
 | |
| 
 | |
|   // If it's not text editable, we don't need to create IMEContentObserver.
 | |
|   if (!IsIMEObserverNeeded(widget->GetInputContext().mIMEState)) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|       ("  CreateIMEContentObserver() doesn't create "
 | |
|        "IMEContentObserver because of non-editable IME state"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(widget->Destroyed())) {
 | |
|     MOZ_LOG(sISMLog, LogLevel::Error,
 | |
|       ("  CreateIMEContentObserver(), FAILED due to "
 | |
|        "the widget for the nsPresContext has gone"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(sPresContext->GetRootWidget() == widget);
 | |
| 
 | |
|   MOZ_LOG(sISMLog, LogLevel::Debug,
 | |
|     ("  CreateIMEContentObserver() is creating an "
 | |
|      "IMEContentObserver instance..."));
 | |
|   sActiveIMEContentObserver = new IMEContentObserver();
 | |
| 
 | |
|   // IMEContentObserver::Init() might create another IMEContentObserver
 | |
|   // instance.  So, sActiveIMEContentObserver would be replaced with new one.
 | |
|   // We should hold the current instance here.
 | |
|   RefPtr<IMEContentObserver> activeIMEContentObserver(sActiveIMEContentObserver);
 | |
|   activeIMEContentObserver->Init(widget, sPresContext, sContent, aEditorBase);
 | |
| }
 | |
| 
 | |
| // static
 | |
| nsresult
 | |
| IMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSelection,
 | |
|                                           nsIContent** aRootContent)
 | |
| {
 | |
|   if (!sActiveIMEContentObserver) {
 | |
|     return NS_ERROR_NOT_AVAILABLE;
 | |
|   }
 | |
|   return sActiveIMEContentObserver->GetSelectionAndRoot(aSelection,
 | |
|                                                         aRootContent);
 | |
| }
 | |
| 
 | |
| // static
 | |
| already_AddRefed<TextComposition>
 | |
| IMEStateManager::GetTextCompositionFor(nsIWidget* aWidget)
 | |
| {
 | |
|   if (!sTextCompositions) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   RefPtr<TextComposition> textComposition =
 | |
|     sTextCompositions->GetCompositionFor(aWidget);
 | |
|   return textComposition.forget();
 | |
| }
 | |
| 
 | |
| // static
 | |
| already_AddRefed<TextComposition>
 | |
| IMEStateManager::GetTextCompositionFor(
 | |
|                    const WidgetCompositionEvent* aCompositionEvent)
 | |
| {
 | |
|   if (!sTextCompositions) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   RefPtr<TextComposition> textComposition =
 | |
|     sTextCompositions->GetCompositionFor(aCompositionEvent);
 | |
|   return textComposition.forget();
 | |
| }
 | |
| 
 | |
| // static
 | |
| already_AddRefed<TextComposition>
 | |
| IMEStateManager::GetTextCompositionFor(nsPresContext* aPresContext)
 | |
| {
 | |
|   if (!sTextCompositions) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   RefPtr<TextComposition> textComposition =
 | |
|     sTextCompositions->GetCompositionFor(aPresContext);
 | |
|   return textComposition.forget();
 | |
| }
 | |
| 
 | |
| } // namespace mozilla
 | 
