/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
sPendingFocusedBrowserSwitchingData.reset();
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();
}
if (sPendingFocusedBrowserSwitchingData.isSome()) {
MOZ_ASSERT(blur ==
sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused); // If focus is not changed between browsers actually, we need to do // nothing here. Let's cancel handling what this method does. if (sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred ==
aFocus) {
sPendingFocusedBrowserSwitchingData.reset();
MOZ_LOG(sISMLog, LogLevel::Info,
(" OnFocusMovedBetweenBrowsers(), canceled all pending focus " "moves between browsers")); return;
}
blur = sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused = aFocus;
MOZ_ASSERT(blur != aFocus);
}
// If application was inactive, but is now activated, and the last focused // this is called by BrowserParent::UnsetTopLevelWebFocusAll() from // nsFocusManager::WindowRaised(). If a content has focus in a remote // process and it has composition, it may get focus back later and the // composition shouldn't be commited now. Therefore, we should put off to // handle this until getting another call of this method or a call of //`OnFocusChangeInternal()`. if (blur && !aFocus && !sIsActive && sTextInputHandlingWidget &&
sTextCompositions &&
sTextCompositions->GetCompositionFor(sTextInputHandlingWidget)) { if (sPendingFocusedBrowserSwitchingData.isNothing()) {
sPendingFocusedBrowserSwitchingData.emplace(blur, aFocus);
}
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusMovedBetweenBrowsers(), put off to handle it until " "next OnFocusChangeInternal() call")); return;
}
sPendingFocusedBrowserSwitchingData.reset();
const nsCOMPtr<nsIWidget> oldWidget = sTextInputHandlingWidget; // In the chrome-process case, we'll get sTextInputHandlingWidget from a // PresShell later.
sTextInputHandlingWidget =
aFocus ? nsCOMPtr<nsIWidget>(aFocus->GetTextInputHandlingWidget()).get()
: nullptr; if (oldWidget && sTextCompositions) {
RefPtr<TextComposition> composition =
sTextCompositions->GetCompositionFor(oldWidget); if (composition) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusMovedBetweenBrowsers(), requesting to commit " "composition to " "the (previous) focused widget (would request=%s)",
TrueOrFalse(!oldWidget->IMENotificationRequestsRef().contains(
IMENotificationRequest::NotifyDuringInactive))));
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
composition->GetBrowserParent());
}
}
// The manager check is to avoid telling the content process to stop // IME state management after focus has already moved there between // two same-process-hosted out-of-process iframes. if (blur && (!aFocus || (blur->Manager() != aFocus->Manager()))) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusMovedBetweenBrowsers(), notifying previous " "focused child process of parent process or another child process " "getting focus"));
blur->StopIMEStateManagement();
}
if (sActiveIMEContentObserver) {
DestroyIMEContentObserver();
}
if (sFocusedIMEWidget) { // sFocusedIMEBrowserParent can be null, if IME focus hasn't been // taken before BrowserParent blur. // `blur` can be null when keyboard focus moves not actually // between tabs but an open menu is involved.
MOZ_ASSERT(!sFocusedIMEBrowserParent || !blur ||
(sFocusedIMEBrowserParent == blur));
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusMovedBetweenBrowsers(), notifying IME of blur"));
NotifyIME(NOTIFY_IME_OF_BLUR, sFocusedIMEWidget, sFocusedIMEBrowserParent);
// We deliberately don't null out sFocusedElement or sFocusedPresContext here. // When focus is in remote content, as far as layout in the chrome process is // concerned, the corresponding content is the top-level XUL browser. Changes // among out-of-process iframes don't change that, so dropping the pointer to // the XUL browser upon such a change would break IME handling.
}
// static void IMEStateManager::WidgetOnQuit(nsIWidget* aWidget) { if (sFocusedIMEWidget == aWidget) {
MOZ_LOG(
sISMLog, LogLevel::Debug,
("WidgetOnQuit(aWidget=0x%p (available %s)), sFocusedIMEWidget=0x%p",
aWidget, TrueOrFalse(aWidget && !aWidget->Destroyed()),
sFocusedIMEWidget)); // Notify IME of blur (which is done by IMEContentObserver::Destroy // automatically) when the widget still has IME focus before forgetting the // focused widget because the focused widget is required to clean up native // IME handler with sending blur notification. Fortunately, the widget // has not been destroyed yet here since some methods to sending blur // notification won't work with destroyed widget.
IMEStateManager::DestroyIMEContentObserver(); // Finally, clean up the widget and related objects for avoiding to leak.
IMEStateManager::WidgetDestroyed(aWidget);
}
}
// NOTE: Don't set input context from here since this has already lost // the rights to change input context.
// The requestee of this method in the main process must destroy its // active IMEContentObserver for making existing composition end and // make it be possible to start new composition in new focused process. // Therefore, we shouldn't notify the main process of any changes which // occurred after here.
AutoRestore<bool> restoreStoppingIMEStateManagementState(
sCleaningUpForStoppingIMEStateManagement);
sCleaningUpForStoppingIMEStateManagement = true;
// 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;
}
TextComposition* const 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;
}
// static
nsresult IMEStateManager::OnDestroyPresContext(nsPresContext& 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 != sFocusedPresContext) { return NS_OK;
}
// static
nsresult IMEStateManager::OnRemoveContent(nsPresContext& aPresContext,
Element& aElement) { // First, if there is a composition in the aElement, clean up it. if (sTextCompositions) { const RefPtr<TextComposition> compositionInContent =
sTextCompositions->GetCompositionInContent(&aPresContext, &aElement);
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 (!sFocusedPresContext || // If focused element is a text control or an editing host, we need to // emulate "blur" on it when it's removed.
(sFocusedElement && sFocusedElement != &aElement) || // If it is (or was) in design mode, we need to emulate "blur" on the // document when the observing element (typically, <body>) is removed.
(!sFocusedElement &&
(!sActiveIMEContentObserver ||
sActiveIMEContentObserver
->GetObservingEditingHostOrTextControlElement() !=
&aElement))) { return NS_OK;
}
MOZ_ASSERT(sFocusedPresContext == &aPresContext);
if (IsIMEObserverNeeded(newState)) { // Initializing IMEContentObserver instance requires Selection, but its // ranges have not been adjusted for this removal. Therefore, we need to // wait a moment.
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( "IMEStateManager::RecreateIMEContentObserverWhenContentRemoved",
[presContext = OwningNonNull{aPresContext}]() {
MOZ_ASSERT(sFocusedPresContext == presContext);
MOZ_ASSERT(!sFocusedElement); if (HTMLEditor* const htmlEditor =
nsContentUtils::GetHTMLEditor(presContext)) {
CreateIMEContentObserver(*htmlEditor, nullptr);
}
}));
}
return NS_OK;
}
// static void IMEStateManager::OnParentChainChangedOfObservingElement(
IMEContentObserver& aObserver, nsIContent& aContent) { if (!sFocusedPresContext || sActiveIMEContentObserver != &aObserver) { return;
} if (Element* const textControlElement =
aObserver.GetObservingTextControlElement()) { // If a text control has focus, the parent chain changed is notified when // the anonymous <div> is removed since aObserver is observing it. However, // we want to be notified only when the focused element, i.e., the text // control itself, is removed. So, when the text control element is not an // inclusive descendant of aContent, we don't need to handle this.
MOZ_ASSERT(textControlElement->IsTextControlElement()); if (!textControlElement->IsInclusiveDescendantOf(&aContent)) { return;
}
} const RefPtr<nsPresContext> presContext = aObserver.GetPresContext(); const RefPtr<Element> editingHostOrTextControlElement =
aObserver.GetObservingEditingHostOrTextControlElement(); if (NS_WARN_IF(!presContext) ||
NS_WARN_IF(!editingHostOrTextControlElement)) { return;
}
MOZ_LOG(sISMLog, LogLevel::Info,
("OnParentChainChangedOfObservingElement(aObserver=0x%p), " "sFocusedPresContext=0x%p, sFocusedElement=0x%p, " "aObserver->GetPresContext()=0x%p, " "aObserver->GetObservingEditingHostOrTextControlElement()=0x%p",
&aObserver, sFocusedPresContext.get(), sFocusedElement.get(),
presContext.get(), editingHostOrTextControlElement.get()));
OnRemoveContent(*presContext, *editingHostOrTextControlElement);
}
if ( // Nothing to do if nobody has focus.
!sFocusedPresContext || !sTextInputHandlingWidget || // Nothing to do if the editable document does not have focus.
sFocusedPresContext != aHTMLEditor.GetPresContext() || // If it's not in the design mode, any mutation should be handled with a // focus change.
!aHTMLEditor.IsInDesignMode() || // Nothing to do if the active IMEContentObserver has already been // observing the new root element.
(aNewRootElement && sActiveIMEContentObserver &&
sActiveIMEContentObserver->GetObservingElement() == aNewRootElement)) { return;
}
if (!aNewRootElement) { // When there is no element in the document, let's disable IME.
IMEState newState = GetNewIMEState(*presContext, nullptr);
MOZ_ASSERT(newState.mEnabled == IMEEnabled::Disabled);
InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
InputContextAction::LOST_FOCUS);
InputContext::Origin origin =
BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
OwningNonNull<nsIWidget> textInputHandlingWidget =
*sTextInputHandlingWidget;
SetIMEState(newState, presContext, nullptr, textInputHandlingWidget, action,
origin); return;
}
MOZ_ASSERT(aNewRootElement); const IMEState newState = GetNewIMEState(*presContext, focusedElement); // The caller wants to enable IME if there is at least one element in the most // cases. However, IME may be disabled, e.g., the menubar key listener is now // installed, etc. Therefore, if the new state is not "enabled", we should // not update the state in unexpected situations. if (MOZ_UNLIKELY(newState.mEnabled != IMEEnabled::Enabled)) {
MOZ_LOG(sISMLog, LogLevel::Warning,
(" OnUpdateHTMLEditorRootElement(): WARNING, Not updating IME " "state because of the new IME state is not \"enabled\"")); return;
}
InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
InputContextAction::GOT_FOCUS);
InputContext::Origin origin =
BrowserParent::GetFocused() ? InputContext::ORIGIN_CONTENT : sOrigin;
OwningNonNull<nsIWidget> textInputHandlingWidget = *sTextInputHandlingWidget;
SetIMEState(newState, presContext, focusedElement, textInputHandlingWidget,
action, origin); // Somebody moved focus, don't keep handling this since we lost the rights // to touch IME state. if (sFocusedPresContext != presContext || sFocusedElement != focusedElement ||
sActiveIMEContentObserver) {
MOZ_LOG(sISMLog, LogLevel::Warning,
("OnUpdateHTMLEditorRootElement(), WARNING: Somebody update focus " "during setting IME state, sFocusedPresContext=0x%p, " "sFocusedElement=%s, sActiveIMEContentObserver=0x%p",
sFocusedPresContext.get(),
ToString(RefPtr<Element>(sFocusedElement)).c_str(),
sActiveIMEContentObserver.get())); return;
}
// static
nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
Element* aElement,
InputContextAction aAction) {
NS_ASSERTION(!aElement || aElement->GetPresContext(
Element::PresContextFor::eForComposedDoc) ==
aPresContext, "aPresContext does not match with one of aElement");
bool remoteHasFocus = EventStateManager::IsRemoteTarget(aElement); // If we've handled focused content, we were inactive but now active, // a remote process has focus, and setting focus to same content in the main // process, it means that we're restoring focus without changing DOM focus // both in the main process and the remote process. constbool restoringContextForRemoteContent =
XRE_IsParentProcess() && remoteHasFocus && !sIsActive && aPresContext &&
sFocusedPresContext && sFocusedElement &&
sFocusedPresContext.get() == aPresContext &&
sFocusedElement.get() == aElement &&
aAction.mFocusChange != InputContextAction::MENU_GOT_PSEUDO_FOCUS;
sIsActive = !!aPresContext; if (sPendingFocusedBrowserSwitchingData.isSome()) {
MOZ_ASSERT(XRE_IsParentProcess());
RefPtr<Element> focusedElement = sFocusedElement;
RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext;
RefPtr<BrowserParent> browserParentBlurred =
sPendingFocusedBrowserSwitchingData.ref().mBrowserParentBlurred;
RefPtr<BrowserParent> browserParentFocused =
sPendingFocusedBrowserSwitchingData.ref().mBrowserParentFocused;
OnFocusMovedBetweenBrowsers(browserParentBlurred, browserParentFocused); // If another call of this method happens during the // OnFocusMovedBetweenBrowsers call, we shouldn't take back focus to // the old one. if (focusedElement != sFocusedElement.get() ||
focusedPresContext != sFocusedPresContext.get()) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnChangeFocusInternal(aPresContext=0x%p, aElement=0x%p) " "stoped handling it because the focused content was changed to " "sFocusedPresContext=0x%p, sFocusedElement=0x%p by another call",
aPresContext, aElement, sFocusedPresContext.get(),
sFocusedElement.get())); return NS_OK;
}
}
// If new aPresShell has been destroyed, this should handle the focus change // as nobody is getting focus.
MOZ_ASSERT_IF(!aPresContext, !aElement); 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;
aElement = nullptr;
} elseif (!aPresContext) {
aElement = nullptr;
}
// 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().contains(
IMENotificationRequest::NotifyDuringInactive)) {
MOZ_LOG(
sISMLog, LogLevel::Info,
(" OnChangeFocusInternal(), requesting to commit composition to " "the (previous) focused widget"));
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
composition->GetBrowserParent());
}
}
}
if (sActiveIMEContentObserver) {
MOZ_ASSERT(!remoteHasFocus || XRE_IsContentProcess(), "IMEContentObserver should have been destroyed by " "OnFocusMovedBetweenBrowsers."); if (!aPresContext) { if (!sActiveIMEContentObserver->KeepAliveDuringDeactive()) {
DestroyIMEContentObserver();
}
} // Otherwise, i.e., new focused content is in this process, let's check // whether the editable content for aElement has already been being observed // by the active IMEContentObserver. If not, let's stop observing the // editable content which is being blurred. elseif (!sActiveIMEContentObserver->IsObserving(*aPresContext, aElement)) {
DestroyIMEContentObserver();
}
}
if (!aPresContext) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnChangeFocusInternal(), no nsPresContext is being activated")); return NS_OK;
}
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.
sTextInputHandlingWidget = 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 = remoteHasFocus ? IMEState(IMEEnabled::Disabled)
: GetNewIMEState(*aPresContext, aElement); bool setIMEState = true;
constauto CanSkipSettingContext = [&](const InputContext& aOldContext) { constauto IsChangingBrowsingMode = [&]() { constbool willBeInPrivateBrowsingMode =
aPresContext && aPresContext->Document() &&
aPresContext->Document()->IsInPrivateBrowsing(); return willBeInPrivateBrowsingMode != aOldContext.mInPrivateBrowsing;
}; constauto IsChangingURI = [&]() { const nsCOMPtr<nsIURI> newURI =
IMEStateManager::GetExposableURL(aPresContext); if (!newURI != !aOldContext.mURI) { returntrue; // One is not exposable URI.
} if (!newURI) {
MOZ_ASSERT(!aOldContext.mURI); returnfalse; // Moved in non-exposable URIs.
} bool same = false; return NS_FAILED(newURI->Equals(aOldContext.mURI, &same)) || !same;
}; return !IsChangingBrowsingMode() && !IsChangingURI();
};
if (remoteHasFocus && XRE_IsParentProcess()) { if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS) { // If menu keyboard listener is installed, we need to disable IME now.
setIMEState = true;
} elseif (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;
}
} elseif (focusActuallyChanging) {
InputContext context = newWidget->GetInputContext(); if (context.mIMEState.mEnabled == IMEEnabled::Disabled &&
context.mOrigin == InputContext::ORIGIN_CONTENT &&
CanSkipSettingContext(context)) {
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"));
}
} elseif (newWidget->GetInputContext().mOrigin !=
InputContext::ORIGIN_MAIN &&
CanSkipSettingContext(newWidget->GetInputContext())) { // 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 &&
CanSkipSettingContext(context)) {
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 (sFocusedPresContext && oldWidget) { // FIXME: During committing composition, focused element may be changed. // In such case, we should not set sFocusedPresContext nor // sFocusedElement below.
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget,
sFocusedIMEBrowserParent);
}
} elseif (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) { // If aElement isn't null or aElement is null but editable, somebody gets // focus. bool gotFocus = aElement || (newState.mEnabled == IMEEnabled::Enabled);
aAction.mFocusChange = gotFocus ? InputContextAction::GOT_FOCUS
: InputContextAction::LOST_FOCUS;
}
// FIXME: If we're in the parent process, the following calls may cause // calling the OS API. Then, anything could happen. So, the focus may have // been changed something other. If so, shouldn't set sFocusedPresContext // nor sFocusedElement below anymore. if (remoteHasFocus && 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, aElement, *newWidget, aAction,
remoteHasFocus ? InputContext::ORIGIN_CONTENT : sOrigin);
}
}
// Don't call CreateIMEContentObserver() here, we'll call it in // UpdateIMEState() after the focused editor is initialized and the focus is // not changed during the initialization.
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnChangeFocusInternal(), modified IME state for " "sFocusedPresContext=0x%p, sFocusedElement=0x%p",
sFocusedPresContext.get(), sFocusedElement.get()));
// Update the state whether the menubar has pseudo focus or not immediately. // This will be referred by the runner which is created below.
sInstalledMenuKeyboardListener = aInstalling; // However, this may be called when it's not safe to run script. Therefore, // we need to create a runnable to notify IME of the pseudo focus change. if (sPseudoFocusChangeRunnable) { return;
}
sPseudoFocusChangeRunnable = new PseudoFocusChangeRunnable(aInstalling);
nsContentUtils::AddScriptRunner(sPseudoFocusChangeRunnable);
}
// Clear the runnable first for nested call of // OnInstalledMenuKeyboardListener().
RefPtr<PseudoFocusChangeRunnable> runningOne =
sPseudoFocusChangeRunnable.forget();
MOZ_ASSERT(!sPseudoFocusChangeRunnable);
// If the requested state is still same, let's make the menubar get // pseudo focus with the latest focused PresContext and Element. // Note that this is no problem even after the focused element is changed // after aCaller is created because only sInstalledMenuKeyboardListener // manages the state and current focused PresContext and element should be // used only for restoring the focus from the menubar. So, restoring // focus with the lasted one does make sense. if (sInstalledMenuKeyboardListener == aSetPseudoFocus) {
InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
aSetPseudoFocus
? InputContextAction::MENU_GOT_PSEUDO_FOCUS
: InputContextAction::MENU_LOST_PSEUDO_FOCUS);
RefPtr<nsPresContext> focusedPresContext = sFocusedPresContext;
RefPtr<Element> focusedElement = sFocusedElement;
OnChangeFocusInternal(focusedPresContext, focusedElement, action); return;
}
// If the requested state is different from current state, we don't need to // make the menubar get and lose pseudo focus because of redundant. However, // if there is a composition on the original focused element, we need to // commit it because pseudo focus move should've caused it. if (!aFocusedPresContextAtRequested) { return;
}
RefPtr<TextComposition> composition =
GetTextCompositionFor(aFocusedPresContextAtRequested); if (!composition) { return;
} if (nsCOMPtr<nsIWidget> widget =
aFocusedPresContextAtRequested->GetTextInputHandlingWidget()) {
composition->RequestToCommit(widget, false);
}
}
if (sFocusedPresContext != &aPresContext || sFocusedElement != aElement) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnMouseButtonEventInEditor(), " "the mouse event isn't fired on the editor managed by ISM")); returnfalse;
}
if (!sActiveIMEContentObserver) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnMouseButtonEventInEditor(), " "there is no active IMEContentObserver")); returnfalse;
}
if (!sActiveIMEContentObserver->IsObserving(aPresContext, aElement)) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnMouseButtonEventInEditor(), " "the active IMEContentObserver isn't managing the editor")); returnfalse;
}
if (!aMouseEvent.IsTrusted()) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnClickInEditor(), " "the mouse event isn't a trusted event")); return; // ignore untrusted event.
}
if (aMouseEvent.mButton) {
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 action(cause, InputContextAction::FOCUS_NOT_CHANGED);
IMEState newState = GetNewIMEState(aPresContext, aElement); // If the state is not editable, there should be no active IMEContentObserver. // However, if this click sets focus to the editor, IMEContentObserver may // have not been created yet. Instead, if there is active IMEContentObserver, // it should be editable.
MOZ_ASSERT_IF(!newState.IsEditable(), !sActiveIMEContentObserver);
MOZ_ASSERT_IF(sActiveIMEContentObserver, newState.IsEditable());
SetIMEState(newState, &aPresContext, aElement, textInputHandlingWidget,
action, sOrigin);
}
if (sFocusedElement == aFocusedElement) { returntrue;
}
// If sFocusedElement is not nullptr, but aFocusedElement is nullptr, it does // not have focus from point of view of IMEStateManager. if (sFocusedElement) { returnfalse;
}
// If the caller does not think that nobody has focus, but we know there is // a focused content, the caller must be called with wrong content. if (!aFocusedElement) { returnfalse;
}
// If the aFocusedElement is in design mode, sFocusedElement may be nullptr. if (aFocusedElement->IsInDesignMode()) {
MOZ_ASSERT(&aPresContext == sFocusedPresContext && !sFocusedElement); returntrue;
}
// Otherwise, only when aFocusedElement is the root element, it can have // focus, but IMEStateManager::OnChangeFocus is called with nullptr for // aFocusedElement if it was not editable. // XXX In this case, should the caller update sFocusedElement? return aFocusedElement->IsEditable() && sFocusedPresContext->Document() &&
sFocusedPresContext->Document()->GetRootElement() == aFocusedElement;
}
if (!IsFocusedElement(aPresContext, aElement)) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusInEditor(), " "an editor not managed by ISM gets focus")); return;
}
MOZ_ASSERT(sTextInputHandlingWidget);
// If the IMEContentObserver instance isn't observing the editable content for // aElement, we need to recreate the instance because the active observer is // observing different content even if aElement keeps being focused. E.g., // it's an <input> and editing host but was not a text control, but now, it's // a text control. if (sActiveIMEContentObserver) { if (sActiveIMEContentObserver->IsObserving(aPresContext, aElement)) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusInEditor(), " "the editable content for aEditorBase has already been being " "observed by sActiveIMEContentObserver")); return;
} // If the IMEContentObserver has not finished initializing itself yet, // we don't need to recreate it because the following // TryToFlushPendingNotifications call must make it initialized. const nsCOMPtr<nsIWidget> textInputHandlingWidget =
sTextInputHandlingWidget; if (!sActiveIMEContentObserver->IsBeingInitializedFor(
aPresContext, aElement, aEditorBase)) {
DestroyIMEContentObserver();
} if (NS_WARN_IF(!IsFocusedElement(aPresContext, aElement)) ||
NS_WARN_IF(!sTextInputHandlingWidget) ||
NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" OnFocusInEditor(), detected unexpected focus change with " "re-initializing active IMEContentObserver")); return;
}
}
if (!sActiveIMEContentObserver && sTextInputHandlingWidget &&
IsIMEObserverNeeded(
sTextInputHandlingWidget->GetInputContext().mIMEState)) {
CreateIMEContentObserver(aEditorBase, aElement); if (sActiveIMEContentObserver) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusInEditor(), new IMEContentObserver is created (0x%p)",
sActiveIMEContentObserver.get()));
}
}
if (sActiveIMEContentObserver) {
sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnFocusInEditor(), trying to send pending notifications in " "the active IMEContentObserver (0x%p)...",
sActiveIMEContentObserver.get()));
}
}
if (NS_WARN_IF(!sTextInputHandlingWidget) ||
NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) { return;
}
// Check if IME has focus. If and only if IME has focus, we may need to // update IME state of the widget to re-open VKB. Otherwise, IME will open // VKB at getting focus. if (!sActiveIMEContentObserver ||
!sActiveIMEContentObserver->IsObserving(aPresContext, &aElement)) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" OnReFocus(), editable content for aElement was not being " "observed by the sActiveIMEContentObserver")); return;
}
MOZ_ASSERT(&aElement == sFocusedElement.get());
if (!UserActivation::IsHandlingUserInput() ||
UserActivation::IsHandlingKeyboardInput()) { return;
}
// Don't update IME state during composition. if (sTextCompositions) { if (const TextComposition* composition =
sTextCompositions->GetCompositionFor(textInputHandlingWidget)) { if (composition->IsComposing()) { return;
}
}
}
const InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
InputContextAction::FOCUS_NOT_CHANGED); const IMEState newState = GetNewIMEState(aPresContext, &aElement); // If aElement has not had a primary frame for it, if (MOZ_UNLIKELY(!newState.IsEditable())) { if (sActiveIMEContentObserver->EditorIsTextEditor()) {
TextControlElement* const textControlElement =
TextControlElement::FromNode(aElement);
MOZ_ASSERT(textControlElement); if (textControlElement &&
textControlElement->IsSingleLineTextControlOrTextArea()) {
MOZ_LOG(
sISMLog, LogLevel::Warning,
(" OnReFocus(), Temporarily disabling IME for the focused element " "because probably the TextControlState could not return " "TextEditor (textEditor: %p)",
textControlElement->GetTextControlState()->GetExtantTextEditor()));
}
} else {
HTMLEditor* const htmlEditor =
nsContentUtils::GetHTMLEditor(&aPresContext); #ifdef DEBUG
MOZ_ASSERT(htmlEditor);
Result<IMEState, nsresult> stateOrError =
htmlEditor->GetPreferredIMEState();
MOZ_ASSERT(stateOrError.isOk());
MOZ_ASSERT(!stateOrError.inspect().IsEditable()); #endif// #ifdef DEBUG
MOZ_LOG(sISMLog, LogLevel::Warning,
(" OnRefocus(), Disabling IME for the focused element, " "HTMLEditor=%p { IsReadonly()=%s }",
htmlEditor,
htmlEditor ? TrueOrFalse(htmlEditor->IsReadonly()) : "N/A"));
}
}
SetIMEState(newState, &aPresContext, &aElement, textInputHandlingWidget,
action, sOrigin);
}
if (sIsGettingNewIMEState) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" MaybeOnEditableStateDisabled(), " "does nothing because of called while getting new IME state")); return;
}
if (&aPresContext != sFocusedPresContext || aElement != sFocusedElement) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" MaybeOnEditableStateDisabled(), " "does nothing because of another element already has focus")); return;
}
if (NS_WARN_IF(!sTextInputHandlingWidget) ||
NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" MaybeOnEditableStateDisabled(), FAILED due to " "the widget for the managing the nsPresContext has gone")); return;
}
const IMEState newIMEState = GetNewIMEState(aPresContext, aElement); // If IME state becomes editable, HTMLEditor should also be initialized with // same path as it gets focus. Therefore, IMEStateManager::UpdateIMEState // should be called by HTMLEditor instead. if (newIMEState.IsEditable()) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" MaybeOnEditableStateDisabled(), " "does nothing because IME state does not become disabled")); return;
}
// Otherwise, disable IME on the widget and destroy active IMEContentObserver // if there is. const InputContext inputContext = textInputHandlingWidget->GetInputContext(); if (inputContext.mIMEState.mEnabled == newIMEState.mEnabled) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" MaybeOnEditableStateDisabled(), " "does nothing because IME state is not changed")); return;
}
if (sActiveIMEContentObserver) {
DestroyIMEContentObserver();
}
if (sIsGettingNewIMEState) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" UpdateIMEState(), " "does nothing because of called while getting new IME state")); return;
}
RefPtr<PresShell> presShell(aEditorBase.GetPresShell()); if (NS_WARN_IF(!presShell)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), FAILED due to " "editor doesn't have PresShell")); return;
}
const RefPtr<nsPresContext> presContext =
aElement
? aElement->GetPresContext(Element::PresContextFor::eForComposedDoc)
: aEditorBase.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 aElement // 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 (sFocusedPresContext != 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(!sFocusedPresContext)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), FAILED due to " "no managing nsPresContext")); return;
}
if (NS_WARN_IF(!sTextInputHandlingWidget) ||
NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), FAILED due to " "the widget for the managing nsPresContext has gone")); return;
}
// TODO: Investigate if we could put off to initialize IMEContentObserver // later because a lot of callers need to be marked as // MOZ_CAN_RUN_SCRIPT otherwise.
// If there is an active observer and we need an observer, we want to keep // using the observer as far as possible because it's already notified IME of // focus. However, between the call of OnChangeFocus() and UpdateIMEState(), // focus and/or IME state may have been changed. If so, we need to update // the observer for current situation. constbool hasActiveObserverAndNeedObserver =
sActiveIMEContentObserver && IsIMEObserverNeeded(aNewIMEState);
// If the active observer was not initialized with aEditorBase, it means // that the old editor lost focus but new editor gets focus **without** // focus move in the DOM tree. This may happen with changing the type of // <input> element, etc. In this case, we need to let IME know a focus move // with recreating IMEContentObserver because editable target has been // changed. constbool needToRecreateObserver =
hasActiveObserverAndNeedObserver &&
!sActiveIMEContentObserver->WasInitializedWith(aEditorBase);
// If the active observer was initialized with same editor, we can/should // keep using it for avoiding to notify IME of a focus change. However, we // may need to re-initialize it because it may have temporarily stopped // observing the content. if (hasActiveObserverAndNeedObserver && !needToRecreateObserver) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" UpdateIMEState(), try to reinitialize the active " "IMEContentObserver")); if (!sActiveIMEContentObserver->MaybeReinitialize(
textInputHandlingWidget, *sFocusedPresContext, aElement,
aEditorBase)) [[unlikely]] {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), failed to reinitialize the active " "IMEContentObserver"));
} if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), widget has gone during re-initializing " "the active IMEContentObserver")); return;
}
}
// If there is no active IMEContentObserver or it isn't observing the // editable content for aElement, we should recreate an observer. E.g., // aElement which is an editing host may have been changed from/to a text // control. constbool createNewObserver =
IsIMEObserverNeeded(aNewIMEState) &&
(!sActiveIMEContentObserver || needToRecreateObserver ||
!sActiveIMEContentObserver->IsObserving(*sFocusedPresContext, aElement)); // If we're recreating new IMEContentObserver or new state does not need // IMEContentObserver, destroy the active one. constbool destroyCurrentObserver =
sActiveIMEContentObserver &&
(createNewObserver || !IsIMEObserverNeeded(aNewIMEState));
// If IME state is changed, e.g., from "enabled" or "password" to "disabled", // we cannot keep the composing state because the new "disabled" state does // not support composition, and also from "enabled" to "password" and vice // versa, IME may use different composing rules. Therefore, for avoiding // IME to be confused, we should fix the composition first. constbool updateIMEState =
aOptions.contains(UpdateIMEStateOption::ForceUpdate) ||
(textInputHandlingWidget->GetInputContext().mIMEState.mEnabled !=
aNewIMEState.mEnabled); if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
MOZ_LOG(
sISMLog, LogLevel::Error,
(" UpdateIMEState(), widget has gone during getting input context")); return;
}
if (updateIMEState &&
!aOptions.contains(UpdateIMEStateOption::DontCommitComposition)) {
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, textInputHandlingWidget,
sFocusedIMEBrowserParent); if (NS_WARN_IF(textInputHandlingWidget->Destroyed())) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), widget has gone during committing " "composition")); return;
} // FIXME: If committing composition changes IME state recursively, we should // not keep updating IME state here. However, how can we manage it? // Is a generation of the state is required?
}
// Notify IME of blur first. if (destroyCurrentObserver) {
DestroyIMEContentObserver(); if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) ||
NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), has set input context, but the widget is " "not focused")); return;
}
}
// Then, notify our IME handler of new IME state. if (updateIMEState) {
InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
InputContextAction::FOCUS_NOT_CHANGED);
RefPtr<nsPresContext> editorPresContext = aEditorBase.GetPresContext(); if (NS_WARN_IF(!editorPresContext)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), nsPresContext for editor has already been " "lost")); return;
}
SetIMEState(aNewIMEState, editorPresContext, aElement,
textInputHandlingWidget, action, sOrigin); if (NS_WARN_IF(textInputHandlingWidget->Destroyed()) ||
NS_WARN_IF(sTextInputHandlingWidget != textInputHandlingWidget)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), has set input context, but the widget is " "not focused")); return;
} if (NS_WARN_IF(
sTextInputHandlingWidget->GetInputContext().mIMEState.mEnabled !=
aNewIMEState.mEnabled)) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), has set input context, but IME enabled " "state was overridden by somebody else")); return;
}
}
NS_ASSERTION(IsFocusedElement(*presContext, aElement), "aElement does not match with sFocusedElement");
// Finally, create new observer if required. if (createNewObserver) { if (!sActiveIMEContentObserver && sFocusedPresContext &&
sTextInputHandlingWidget) { // 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, aElement);
} else {
MOZ_LOG(sISMLog, LogLevel::Error,
(" UpdateIMEState(), wanted to create IMEContentObserver, but " "lost focus"));
}
}
}
if (!CanHandleWith(&aPresContext)) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" GetNewIMEState() returns IMEEnabled::Disabled because " "the nsPresContext has been destroyed")); return IMEState(IMEEnabled::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 IMEEnabled::Disabled because " "the nsPresContext is for print or print preview")); return IMEState(IMEEnabled::Disabled);
}
if (sInstalledMenuKeyboardListener) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" GetNewIMEState() returns IMEEnabled::Disabled because " "menu keyboard listener was installed")); return IMEState(IMEEnabled::Disabled);
}
if (!aElement) { // Even if there are no focused content, the focused document might be // editable, such case is design mode. if (aPresContext.Document() && aPresContext.Document()->IsInDesignMode()) { if (aPresContext.Document()->GetRootElement()) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" GetNewIMEState() returns IMEEnabled::Enabled because " "design mode editor has focus")); return IMEState(IMEEnabled::Enabled);
}
MOZ_LOG(sISMLog, LogLevel::Debug,
(" GetNewIMEState() returns IMEEnabled::Disabled because " "document is in the design mode but has no element")); return IMEState(IMEEnabled::Disabled);
}
MOZ_LOG(sISMLog, LogLevel::Debug,
(" GetNewIMEState() returns IMEEnabled::Disabled because " "no content has focus")); return IMEState(IMEEnabled::Disabled);
}
// If aElement is in designMode, aElement should be the root node of the // document. if (aElement && aElement->IsInDesignMode()) {
MOZ_LOG(sISMLog, LogLevel::Debug,
(" GetNewIMEState() returns IMEEnabled::Enabled because " "a content node in design mode editor has focus")); return IMEState(IMEEnabled::Enabled);
}
// 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;
if (aBrowserParent != BrowserParent::GetFocused()) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" SetInputContextForChildProcess(), FAILED, " "because non-focused tab parent tries to set input context")); return;
}
if (NS_WARN_IF(!CanHandleWith(sFocusedPresContext))) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" SetInputContextForChildProcess(), FAILED, " "due to no focused presContext")); return;
}
if (NS_WARN_IF(!sTextInputHandlingWidget) ||
NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" SetInputContextForChildProcess(), FAILED, " "due to the widget for the nsPresContext has gone")); return;
}
// 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;
}
// XXX We don't consider next element is date/time control yet.
nsGenericHTMLElement* nextElement =
nsGenericHTMLElement::FromNodeOrNull(nextContent); if (!nextElement) { returnfalse;
}
// FIXME: Should probably use nsIFrame::IsFocusable if possible, to account // for things like visibility: hidden or so. if (!nextElement->IsFocusableWithoutStyle()) { returnfalse;
}
if (aState.IsEditable()) {
nsGenericHTMLElement::FromNode(&aContent)->GetEnterKeyHint(aActionHint);
// If enterkeyhint is set, we don't infer action hint. if (!aActionHint.IsEmpty()) { return;
}
}
if (!aContent.IsHTMLElement(nsGkAtoms::input)) { return;
}
// Get the input content corresponding to the focused node, // which may be an anonymous child of the input content.
MOZ_ASSERT(&aContent == aContent.FindFirstNonChromeOnlyAccessContent()); const HTMLInputElement* inputElement = HTMLInputElement::FromNode(aContent); if (!inputElement) { return;
}
// If we don't have an action hint and // return won't submit the form, use "maybenext". bool willSubmit = false; bool isLastElement = false;
HTMLFormElement* formElement = inputElement->GetFormInternal(); // is this a form and does it have a default submit element? if (formElement) { if (formElement->IsLastActiveElement(inputElement)) {
isLastElement = true;
}
if (formElement->GetDefaultSubmitElement()) {
willSubmit = true; // is this an html form...
} else { // ... and does it only have a single text input element ? if (!formElement->ImplicitSubmissionIsDisabled() || // ... or is this the last non-disabled element?
isLastElement) {
willSubmit = true;
}
}
}
if (!isLastElement && formElement) { // If next tabbable content in form is text control, hint should be "next" // even there is submit in form. // MOZ_KnownLive(inputElement) because it's an alias of aContent. if (IsNextFocusableElementTextControl(MOZ_KnownLive(inputElement))) { // This is focusable text control // XXX What good hint for read only field?
aActionHint.AssignLiteral("maybenext"); return;
}
}
if (!willSubmit) {
aActionHint.Truncate(); return;
}
if (inputElement->ControlType() == FormControlType::InputSearch) {
aActionHint.AssignLiteral("search"); return;
}
aActionHint.AssignLiteral("go");
}
staticvoid GetInputMode(const IMEState& aState, const nsIContent& aContent,
nsAString& aInputMode) { if (aState.IsEditable()) {
aContent.AsElement()->GetAttr(nsGkAtoms::inputmode, aInputMode); if (aContent.IsHTMLElement(nsGkAtoms::input) &&
aInputMode.EqualsLiteral("mozAwesomebar")) { if (!nsContentUtils::IsChromeDoc(aContent.OwnerDoc())) { // mozAwesomebar should be allowed only in chrome
aInputMode.Truncate();
}
} else {
ToLowerCase(aInputMode);
}
}
}
staticbool GetAutocorrect(const IMEState& aState, const Element& aElement, const InputContext& aInputContext) { if (!StaticPrefs::dom_forms_autocorrect()) { #ifdef ANDROID // Autocorrect was on-by-default on Android by bug 806349, despite // autocorrect preference. returntrue; #else returnfalse; #endif
}
if (aElement.IsHTMLElement() && aState.IsEditable()) { if (nsContentUtils::IsChromeDoc(aElement.OwnerDoc()) &&
!aElement.HasAttr(nsGkAtoms::autocorrect)) { // Since Chrome UI may not want to enable autocorrect by default such as // bug 1881783. returnfalse;
}
// static
already_AddRefed<nsIURI> IMEStateManager::GetExposableURL( const nsPresContext* aPresContext) { if (!aPresContext) { return nullptr;
}
nsIURI* uri = aPresContext->Document()->GetDocumentURI(); if (!uri) { return nullptr;
} // We don't need to and should not expose special URLs such as: // about: Any apps like IME should work normally and constantly in any // default pages such as about:blank, about:home, etc in either // the main process or a content process. // blob: This may contain big data. If we copy it to the main process, // it may make the heap dirty which makes the process slower. // chrome: Same as about, any apps should work normally and constantly in // any chrome documents. // data: Any native apps in the environment shouldn't change the behavior // with the data URL's content and it may contain too big data. // file: The file path may contain private things and we shouldn't let // other apps like IME know which one is touched by the user because // malicious text services may like files which are explicitly used // by the user better. if (!net::SchemeIsHttpOrHttps(uri)) { return nullptr;
} // Note that we don't need to expose UserPass, Query and Reference to // IME since they may contain sensitive data, but non-malicious text // services must not require these data.
nsCOMPtr<nsIURI> exposableURL; if (NS_FAILED(NS_MutateURI(uri)
.SetQuery(""_ns)
.SetRef(""_ns)
.SetUserPass(""_ns)
.Finalize(exposableURL))) { return nullptr;
}
if (NS_WARN_IF(!aCompositionEvent->IsTrusted()) ||
NS_WARN_IF(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,
aBrowserParent, 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);
}
}
}
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 browserParent.
TextComposition::HandleSelectionEvent(aPresContext, browserParent,
aSelectionEvent);
}
}
// static void IMEStateManager::OnCompositionEventDiscarded(
WidgetCompositionEvent* aCompositionEvent) { // Note that this method is never called for synthesized events for emulating // commit or cancel composition.
// 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);
}
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: {
MOZ_ASSERT(CanSendNotificationToWidget());
// If focus notification comes from a remote browser which already lost // focus, we shouldn't accept the focus notification. Then, following // notifications from the process will be ignored. if (aBrowserParent != BrowserParent::GetFocused()) {
MOZ_LOG(sISMLog, LogLevel::Warning,
(" NotifyIME(), WARNING, the received focus notification is " "ignored, because its associated BrowserParent did not match" "the focused BrowserParent.")); return NS_OK;
} // If IME focus is already set, first blur the currently-focused // IME widget if (sFocusedIMEWidget) { // XXX Why don't we first request the previously-focused IME // widget to commit the composition?
MOZ_ASSERT(
sFocusedIMEBrowserParent || aBrowserParent, "This case shouldn't be caused by focus move in this process");
MOZ_LOG(sISMLog, LogLevel::Warning,
(" NotifyIME(), WARNING, received focus notification with ") "non-null sFocusedIMEWidget. How come " "OnFocusMovedBetweenBrowsers did not blur it already?");
nsCOMPtr<nsIWidget> focusedIMEWidget(sFocusedIMEWidget);
sFocusedIMEWidget = nullptr;
sFocusedIMEBrowserParent = nullptr;
focusedIMEWidget->NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR));
} #ifdef DEBUG if (aBrowserParent) {
nsCOMPtr<nsIWidget> browserParentWidget =
aBrowserParent->GetTextInputHandlingWidget();
MOZ_ASSERT(browserParentWidget == aWidget);
} else {
MOZ_ASSERT(sFocusedPresContext);
MOZ_ASSERT_IF(
sFocusedPresContext->GetTextInputHandlingWidget(),
sFocusedPresContext->GetTextInputHandlingWidget() == aWidget);
} #endif
sFocusedIMEBrowserParent = aBrowserParent;
sFocusedIMEWidget = aWidget;
nsCOMPtr<nsIWidget> widget(aWidget);
MOZ_LOG(
sISMLog, LogLevel::Info,
(" NotifyIME(), about to call widget->NotifyIME() for IME focus")); return widget->NotifyIME(aNotification);
} case NOTIFY_IME_OF_BLUR: { if (aBrowserParent != sFocusedIMEBrowserParent) {
MOZ_LOG(sISMLog, LogLevel::Warning,
(" NotifyIME(), WARNING, the received blur notification is " "ignored " "because it's not from current focused IME browser")); 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;
sFocusedIMEBrowserParent = nullptr; return CanSendNotificationToWidget()
? focusedIMEWidget->NotifyIME(
IMENotification(NOTIFY_IME_OF_BLUR))
: NS_OK;
} 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 (aBrowserParent != sFocusedIMEBrowserParent) {
MOZ_LOG(
sISMLog, LogLevel::Warning,
(" NotifyIME(), WARNING, the received content change notification " "is ignored because it's not from current focused IME browser")); 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;
} if (!CanSendNotificationToWidget()) { 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 (aBrowserParent != composition->GetBrowserParent()) {
MOZ_LOG(
sISMLog, LogLevel::Warning,
(" NotifyIME(), WARNING, the request to IME is ignored because " "it does not come from the remote browser 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,
BrowserParent* aBrowserParent) {
MOZ_LOG(sISMLog, LogLevel::Info,
("NotifyIME(aMessage=%s, aPresContext=0x%p, aBrowserParent=0x%p)",
ToChar(aMessage), aPresContext, aBrowserParent)); // This assertion is just for clarify which message may be set, so feel free // to add other messages if you want.
MOZ_ASSERT(aMessage == REQUEST_TO_CANCEL_COMPOSITION ||
aMessage == REQUEST_TO_COMMIT_COMPOSITION ||
aMessage == NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED); // However, these messages require additional information. Therefore, this // overload shouldn't be used for them.
MOZ_ASSERT(aMessage != NOTIFY_IME_OF_FOCUS &&
aMessage != NOTIFY_IME_OF_BLUR &&
aMessage != NOTIFY_IME_OF_TEXT_CHANGE &&
aMessage != NOTIFY_IME_OF_SELECTION_CHANGE &&
aMessage != NOTIFY_IME_OF_MOUSE_BUTTON_EVENT);
if (NS_WARN_IF(!CanHandleWith(aPresContext))) { return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIWidget> widget =
aPresContext == sFocusedPresContext && sTextInputHandlingWidget
? sTextInputHandlingWidget
: aPresContext->GetTextInputHandlingWidget(); 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, aBrowserParent);
}
if (NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
MOZ_LOG(sISMLog, LogLevel::Error,
(" CreateIMEContentObserver(), FAILED due to " "the widget for the nsPresContext has gone")); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.