Bug 1053053 part.1 Active TabParent should be managed by IMEStateManager r=smaug

This commit is contained in:
Masayuki Nakano 2015-07-01 22:19:11 +09:00
parent 6910333690
commit b95cd67450
5 changed files with 206 additions and 118 deletions

View File

@ -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

View File

@ -183,6 +183,7 @@ GetNotifyIMEMessageName(IMEMessage aMessage)
nsIContent* IMEStateManager::sContent = nullptr;
nsPresContext* IMEStateManager::sPresContext = nullptr;
StaticRefPtr<nsIWidget> IMEStateManager::sFocusedIMEWidget;
StaticRefPtr<TabParent> 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<TabParent> 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<nsIWidget> 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<nsIWidget> 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

View File

@ -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<nsIWidget> sFocusedIMEWidget;
static StaticRefPtr<TabParent> sActiveTabParent;
static bool sInstalledMenuKeyboardListener;
static bool sIsGettingNewIMEState;
static bool sCheckForIMEUnawareWebApps;

View File

@ -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<nsFrameLoader> frameLoader = GetFrameLoader(true);
nsCOMPtr<nsIObserverService> 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, &notification);
@ -2388,26 +2385,6 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
const int32_t& aCause,
const int32_t& aFocusChange)
{
nsCOMPtr<nsIWidget> 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<int32_t>(IMEState::DISABLED) ? this : nullptr;
InputContext context;
context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
@ -2419,15 +2396,8 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
InputContextAction action(
static_cast<InputContextAction::Cause>(aCause),
static_cast<InputContextAction::FocusChange>(aFocusChange));
widget->SetInputContext(context, action);
nsCOMPtr<nsIObserverService> 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<nsIContent> focusedContent = fm->GetFocusedContent();
if (focusedContent && focusedContent->IsEditable())
return false;
return true;
}
already_AddRefed<nsFrameLoader>
TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
{

View File

@ -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<nsIBrowserDOMWindow> 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;