From b95cd674507fef14ce267f0fd8c646e9317503ff Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 1 Jul 2015 22:19:11 +0900 Subject: [PATCH] Bug 1053053 part.1 Active TabParent should be managed by IMEStateManager r=smaug --- dom/events/EventStateManager.cpp | 4 +- dom/events/IMEStateManager.cpp | 240 ++++++++++++++++++++++--------- dom/events/IMEStateManager.h | 26 ++++ dom/ipc/TabParent.cpp | 49 +------ dom/ipc/TabParent.h | 5 - 5 files changed, 206 insertions(+), 118 deletions(-) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 97ce67206d4..236d81c076d 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3385,7 +3385,7 @@ EventStateManager::RemoteQueryContentEvent(WidgetEvent* aEvent) TabParent* EventStateManager::GetCrossProcessTarget() { - return TabParent::GetIMETabParent(); + return IMEStateManager::GetActiveTabParent(); } bool @@ -3396,7 +3396,7 @@ EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent) nsIContent *focusedContent = GetFocusedContent(); if (focusedContent && focusedContent->IsEditable()) return false; - return TabParent::GetIMETabParent() != nullptr; + return IMEStateManager::GetActiveTabParent() != nullptr; } void diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp index 66877d9b718..a6cdbe16cce 100644 --- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -183,6 +183,7 @@ GetNotifyIMEMessageName(IMEMessage aMessage) nsIContent* IMEStateManager::sContent = nullptr; nsPresContext* IMEStateManager::sPresContext = nullptr; StaticRefPtr IMEStateManager::sFocusedIMEWidget; +StaticRefPtr IMEStateManager::sActiveTabParent; bool IMEStateManager::sInstalledMenuKeyboardListener = false; bool IMEStateManager::sIsGettingNewIMEState = false; bool IMEStateManager::sCheckForIMEUnawareWebApps = false; @@ -221,6 +222,24 @@ IMEStateManager::Shutdown() sTextCompositions = nullptr; } +// static +void +IMEStateManager::OnTabParentDestroying(TabParent* aTabParent) +{ + if (sActiveTabParent != aTabParent) { + return; + } + MOZ_LOG(sISMLog, LogLevel::Info, + ("ISM: IMEStateManager::OnTabParentDestroying(aTabParent=0x%p), " + "The active TabParent is being destroyed", aTabParent)); + + // The active remote process might have crashed. + sActiveTabParent = nullptr; + + // TODO: Need to cancel composition without TextComposition and make + // disable IME. +} + // static nsresult IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) @@ -268,6 +287,7 @@ IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) } NS_IF_RELEASE(sContent); sPresContext = nullptr; + sActiveTabParent = nullptr; return NS_OK; } @@ -325,6 +345,7 @@ IMEStateManager::OnRemoveContent(nsPresContext* aPresContext, NS_IF_RELEASE(sContent); sPresContext = nullptr; + sActiveTabParent = nullptr; return NS_OK; } @@ -350,16 +371,22 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, nsIContent* aContent, InputContextAction aAction) { + nsRefPtr newTabParent = TabParent::GetFrom(aContent); + MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::OnChangeFocusInternal(aPresContext=0x%p, " - "aContent=0x%p, aAction={ mCause=%s, mFocusChange=%s }), " - "sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p", - aPresContext, aContent, GetActionCauseName(aAction.mCause), + "aContent=0x%p (TabParent=0x%p), aAction={ mCause=%s, mFocusChange=%s }), " + "sPresContext=0x%p, sContent=0x%p, sActiveTabParent=0x%p, " + "sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s", + aPresContext, aContent, newTabParent.get(), + GetActionCauseName(aAction.mCause), GetActionFocusChangeName(aAction.mFocusChange), - sPresContext, sContent, sActiveIMEContentObserver)); + sPresContext, sContent, sActiveTabParent.get(), sActiveIMEContentObserver, + GetBoolName(sInstalledMenuKeyboardListener))); bool focusActuallyChanging = - (sContent != aContent || sPresContext != aPresContext); + (sContent != aContent || sPresContext != aPresContext || + sActiveTabParent != newTabParent); nsCOMPtr oldWidget = sPresContext ? sPresContext->GetRootWidget() : nullptr; @@ -394,56 +421,74 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, return NS_OK; } - IMEState newState = GetNewIMEState(aPresContext, aContent); + // 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; - // In e10s, remote content may have IME focus. The main process (i.e. this process) - // would attempt to set state to DISABLED if, for example, the user clicks - // some other remote content. The content process would later re-ENABLE IME, meaning - // that all state-changes were unnecessary. - // Here we filter the common case where the main process knows that the remote - // process controls IME focus. The DISABLED->re-ENABLED progression can - // still happen since remote content may be concurrently communicating its claim - // on focus to the main process... but this cannot cause bugs like missed keypresses. - // (It just means a lot of needless IPC.) - if ((newState.mEnabled == IMEState::DISABLED) && TabParent::GetIMETabParent()) { - MOZ_LOG(sISMLog, LogLevel::Debug, - ("ISM: IMEStateManager::OnChangeFocusInternal(), " - "Parent process cancels to set DISABLED state because the content process " - "has IME focus and has already sets IME state")); - MOZ_ASSERT(XRE_IsParentProcess(), - "TabParent::GetIMETabParent() should never return non-null value " - "in the content process"); - return NS_OK; - } - - if (!focusActuallyChanging) { - // actual focus isn't changing, but if IME enabled state is changing, - // we should do it. - InputContext context = widget->GetInputContext(); - if (context.mIMEState.mEnabled == newState.mEnabled) { + if (newTabParent) { + if (focusActuallyChanging) { + InputContext context = widget->GetInputContext(); + if (context.mIMEState.mEnabled == IMEState::DISABLED) { + setIMEState = false; + MOZ_LOG(sISMLog, LogLevel::Debug, + ("ISM: IMEStateManager::OnChangeFocusInternal(), doesn't set IME " + "state because focused element (or document) is in a child process " + "and the IME state is already disabled")); + } else { + MOZ_LOG(sISMLog, LogLevel::Debug, + ("ISM: IMEStateManager::OnChangeFocusInternal(), will disable IME " + "until new focused element (or document) in the child process " + "will get focus actually")); + } + } else { + // When focus is NOT changed actually, we shouldn't set IME state 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, - ("ISM: IMEStateManager::OnChangeFocusInternal(), " - "neither focus nor IME state is changing")); - return NS_OK; + ("ISM: IMEStateManager::OnChangeFocusInternal(), doesn't set IME " + "state because focused element (or document) is already in the child " + "process")); } - 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); - } - } 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; } - // Update IME state for new focus widget - SetIMEState(newState, aContent, widget, aAction); + if (setIMEState) { + if (!focusActuallyChanging) { + // actual focus isn't changing, but if IME enabled state is changing, + // we should do it. + InputContext context = widget->GetInputContext(); + if (context.mIMEState.mEnabled == newState.mEnabled) { + MOZ_LOG(sISMLog, LogLevel::Debug, + ("ISM: IMEStateManager::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); + } + } 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; + } + + // Update IME state for new focus widget + SetIMEState(newState, aContent, widget, aAction); + } + + sActiveTabParent = newTabParent; sPresContext = aPresContext; if (sContent != aContent) { NS_IF_RELEASE(sContent); @@ -842,6 +887,55 @@ MayBeIMEUnawareWebApp(nsINode* aNode) return haveKeyEventsListener; } +// static +void +IMEStateManager::SetInputContextForChildProcess( + TabParent* aTabParent, + const InputContext& aInputContext, + const InputContextAction& aAction) +{ + MOZ_LOG(sISMLog, LogLevel::Info, + ("ISM: IMEStateManager::SetInputContextForChildProcess(aTabParent=0x%p, " + "aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, " + "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, " + "aAction={ mCause=%s, mAction=%s }, aTabParent=0x%p), sPresContext=0x%p, " + "sActiveTabParent=0x%p", + aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled), + GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen), + NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(), + NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(), + NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(), + GetActionCauseName(aAction.mCause), + GetActionFocusChangeName(aAction.mFocusChange), + sPresContext, sActiveTabParent.get())); + + if (NS_WARN_IF(aTabParent != sActiveTabParent)) { + MOZ_LOG(sISMLog, LogLevel::Error, + ("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, " + "because non-focused tab parent tries to set input context")); + return; + } + + if (NS_WARN_IF(!sPresContext)) { + MOZ_LOG(sISMLog, LogLevel::Error, + ("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, " + "due to no focused presContext")); + return; + } + + nsCOMPtr widget = sPresContext->GetRootWidget(); + if (NS_WARN_IF(!widget)) { + MOZ_LOG(sISMLog, LogLevel::Error, + ("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, " + "due to no widget in the focused presContext")); + return; + } + + MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT); + + SetInputContext(widget, aInputContext, aAction); +} + // static void IMEStateManager::SetIMEState(const IMEState& aState, @@ -851,9 +945,11 @@ IMEStateManager::SetIMEState(const IMEState& aState, { MOZ_LOG(sISMLog, LogLevel::Info, ("ISM: IMEStateManager::SetIMEState(aState={ mEnabled=%s, mOpen=%s }, " - "aContent=0x%p, aWidget=0x%p, aAction={ mCause=%s, mFocusChange=%s })", + "aContent=0x%p (TabParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, " + "mFocusChange=%s })", GetIMEStateEnabledName(aState.mEnabled), - GetIMEStateSetOpenName(aState.mOpen), aContent, aWidget, + GetIMEStateSetOpenName(aState.mOpen), aContent, + TabParent::GetFrom(aContent), aWidget, GetActionCauseName(aAction.mCause), GetActionFocusChangeName(aAction.mFocusChange))); @@ -936,27 +1032,41 @@ IMEStateManager::SetIMEState(const IMEState& aState, aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME; } + SetInputContext(aWidget, context, aAction); +} - MOZ_LOG(sISMLog, LogLevel::Debug, - ("ISM: IMEStateManager::SetIMEState(), " - "calling nsIWidget::SetInputContext(context={ mIMEState={ mEnabled=%s, " - "mOpen=%s }, mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", " - "mActionHint=\"%s\" }, aAction={ mCause=%s, mAction=%s })", - GetIMEStateEnabledName(context.mIMEState.mEnabled), - GetIMEStateSetOpenName(context.mIMEState.mOpen), - NS_ConvertUTF16toUTF8(context.mHTMLInputType).get(), - NS_ConvertUTF16toUTF8(context.mHTMLInputInputmode).get(), - NS_ConvertUTF16toUTF8(context.mActionHint).get(), +// static +void +IMEStateManager::SetInputContext(nsIWidget* aWidget, + const InputContext& aInputContext, + const InputContextAction& aAction) +{ + MOZ_LOG(sISMLog, LogLevel::Info, + ("ISM: IMEStateManager::SetInputContext(aWidget=0x%p, aInputContext={ " + "mIMEState={ mEnabled=%s, mOpen=%s }, mHTMLInputType=\"%s\", " + "mHTMLInputInputmode=\"%s\", mActionHint=\"%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(), GetActionCauseName(aAction.mCause), - GetActionFocusChangeName(aAction.mFocusChange))); + GetActionFocusChangeName(aAction.mFocusChange), + sActiveTabParent.get())); - aWidget->SetInputContext(context, aAction); - if (oldContext.mIMEState.mEnabled == context.mIMEState.mEnabled) { + MOZ_RELEASE_ASSERT(aWidget); + + InputContext oldContext = aWidget->GetInputContext(); + + aWidget->SetInputContext(aInputContext, aAction); + if (oldContext.mIMEState.mEnabled == aInputContext.mIMEState.mEnabled) { return; } nsContentUtils::AddScriptRunner( - new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled)); + new IMEEnabledStateChangedEvent(aInputContext.mIMEState.mEnabled)); } // static diff --git a/dom/events/IMEStateManager.h b/dom/events/IMEStateManager.h index 799e6b1e148..3dde9410a6a 100644 --- a/dom/events/IMEStateManager.h +++ b/dom/events/IMEStateManager.h @@ -9,6 +9,7 @@ #include "mozilla/EventForwards.h" #include "mozilla/StaticPtr.h" +#include "mozilla/dom/TabParent.h" #include "nsIWidget.h" class nsIContent; @@ -33,6 +34,7 @@ class TextComposition; class IMEStateManager { + typedef dom::TabParent TabParent; typedef widget::IMEMessage IMEMessage; typedef widget::IMENotification IMENotification; typedef widget::IMEState IMEState; @@ -43,6 +45,26 @@ public: static void Init(); static void Shutdown(); + /** + * GetActiveTabParent() returns a pointer to a TabParent instance which is + * managed by the focused content (sContent). If the focused content isn't + * managing another process, this returns nullptr. + */ + static TabParent* GetActiveTabParent() { return sActiveTabParent.get(); } + + /** + * OnTabParentDestroying() is called when aTabParent is being destroyed. + */ + static void OnTabParentDestroying(TabParent* aTabParent); + + /** + * SetIMEContextForChildProcess() is called when aTabParent receives + * SetInputContext() from the remote process. + */ + static void SetInputContextForChildProcess(TabParent* aTabParent, + const InputContext& aInputContext, + const InputContextAction& aAction); + static nsresult OnDestroyPresContext(nsPresContext* aPresContext); static nsresult OnRemoveContent(nsPresContext* aPresContext, nsIContent* aContent); @@ -163,6 +185,9 @@ protected: nsIContent* aContent, nsIWidget* aWidget, InputContextAction aAction); + static void SetInputContext(nsIWidget* aWidget, + const InputContext& aInputContext, + const InputContextAction& aAction); static IMEState GetNewIMEState(nsPresContext* aPresContext, nsIContent* aContent); @@ -177,6 +202,7 @@ protected: static nsIContent* sContent; static nsPresContext* sPresContext; static StaticRefPtr sFocusedIMEWidget; + static StaticRefPtr sActiveTabParent; static bool sInstalledMenuKeyboardListener; static bool sIsGettingNewIMEState; static bool sCheckForIMEUnawareWebApps; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 80112401bfb..52e22f5b617 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -248,7 +248,6 @@ private: namespace mozilla { namespace dom { -TabParent *TabParent::mIMETabParent = nullptr; TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr; NS_IMPL_ISUPPORTS(TabParent, @@ -481,9 +480,8 @@ TabParent::Recv__delete__() void TabParent::ActorDestroy(ActorDestroyReason why) { - if (mIMETabParent == this) { - mIMETabParent = nullptr; - } + IMEStateManager::OnTabParentDestroying(this); + nsRefPtr frameLoader = GetFrameLoader(true); nsCOMPtr os = services::GetObserverService(); if (frameLoader) { @@ -1934,7 +1932,6 @@ TabParent::RecvNotifyIMEFocus(const bool& aFocus, return true; } - mIMETabParent = aFocus ? this : nullptr; IMENotification notification(aFocus ? NOTIFY_IME_OF_FOCUS : NOTIFY_IME_OF_BLUR); mContentCache.AssignContent(aContentCache, ¬ification); @@ -2388,26 +2385,6 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled, const int32_t& aCause, const int32_t& aFocusChange) { - nsCOMPtr widget = GetWidget(); - if (!widget || !AllowContentIME()) { - return true; - } - - InputContext oldContext = widget->GetInputContext(); - - // Ignore if current widget IME setting is not DISABLED and didn't come - // from remote content. Chrome content may have taken over. - if (oldContext.mIMEState.mEnabled != IMEState::DISABLED && - oldContext.IsOriginMainProcess()) { - return true; - } - - // mIMETabParent (which is actually static) tracks which if any TabParent has IMEFocus - // When the input mode is set to anything but IMEState::DISABLED, - // mIMETabParent should be set to this - mIMETabParent = - aIMEEnabled != static_cast(IMEState::DISABLED) ? this : nullptr; - InputContext context; context.mIMEState.mEnabled = static_cast(aIMEEnabled); context.mIMEState.mOpen = static_cast(aIMEOpen); @@ -2419,15 +2396,8 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled, InputContextAction action( static_cast(aCause), static_cast(aFocusChange)); - widget->SetInputContext(context, action); - nsCOMPtr observerService = mozilla::services::GetObserverService(); - if (!observerService) - return true; - - nsAutoString state; - state.AppendInt(aIMEEnabled); - observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get()); + IMEStateManager::SetInputContextForChildProcess(this, context, action); return true; } @@ -2617,19 +2587,6 @@ TabParent::RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame, return true; } -bool -TabParent::AllowContentIME() -{ - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - NS_ENSURE_TRUE(fm, false); - - nsCOMPtr focusedContent = fm->GetFocusedContent(); - if (focusedContent && focusedContent->IsEditable()) - return false; - - return true; -} - already_AddRefed TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index f757475b0c6..ef29c96716d 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -366,7 +366,6 @@ public: NS_DECL_NSIAUTHPROMPTPROVIDER NS_DECL_NSISECUREBROWSERUI - static TabParent *GetIMETabParent() { return mIMETabParent; } bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent); bool SendCompositionEvent(mozilla::WidgetCompositionEvent& event); bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event); @@ -447,8 +446,6 @@ protected: Element* mFrameElement; nsCOMPtr mBrowserDOMWindow; - bool AllowContentIME(); - virtual PRenderFrameParent* AllocPRenderFrameParent() override; virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override; @@ -467,8 +464,6 @@ protected: void SetHasContentOpener(bool aHasContentOpener); - // IME - static TabParent *mIMETabParent; ContentCacheInParent mContentCache; nsIntRect mRect;