commit c20c1d810978ac4c7240a88a11fd212396c80f1a
parent c8bb56ecc8e65c3a742bc5efadbe31f18e65a610
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date: Mon, 1 Dec 2025 23:03:12 +0000
Bug 2001825 - Make `IMEStateManager::OnReFocus()` stop asserting editable state r=m_kato
`Element.focus()` may be called while the element does not have a
primary frame. Then, its `TextControlState` does not create
`TextEditor` without a bound frame [1] because an element which is
completely invisible (e.g., `display:none`) should not have
`TextEditor`. Therefore, `GetDesiredIMEState()` of
`nsGenericHTMLFormControlElement` delegates to `nsIContent`'s and return
`IMEEnabled::Disabled`. Finally, `IMEStateManager::OnReFocus()`
asserts the editable state [2] and may fail.
Fortunately, `TextEditor` will be reinialized after a reframe of the
text control element. Then, `EditorBase` will update IME state to make
it available [3]. So, `IMEStateManager::OnReFocus()` does not need to
assert it at the moment.
1. https://searchfox.org/firefox-main/rev/f6e3ccb5853c46f917578697a488d48c3bc09ac8/dom/html/TextControlState.cpp#1662-1666
2. https://searchfox.org/firefox-main/rev/f6e3ccb5853c46f917578697a488d48c3bc09ac8/dom/events/IMEStateManager.cpp#1312
3. https://searchfox.org/firefox-main/rev/f6e3ccb5853c46f917578697a488d48c3bc09ac8/editor/libeditor/EditorBase.cpp#469-470,475
Differential Revision: https://phabricator.services.mozilla.com/D274385
Diffstat:
2 files changed, 73 insertions(+), 5 deletions(-)
diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp
@@ -43,6 +43,7 @@
#include "nsIURI.h"
#include "nsIURIMutator.h"
#include "nsPresContext.h"
+#include "nsTextControlFrame.h"
#include "nsThreadUtils.h"
namespace mozilla {
@@ -678,6 +679,14 @@ nsresult IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
BrowserParent::GetFocused(), sActiveIMEContentObserver.get(),
TrueOrFalse(sInstalledMenuKeyboardListener), TrueOrFalse(sIsActive),
TrueOrFalse(restoringContextForRemoteContent)));
+ if (aElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" aElement: %s", ToString(*aElement).c_str()));
+ }
+ if (sFocusedElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" sFocusedElement: %s", ToString(*sFocusedElement).c_str()));
+ }
sIsActive = !!aPresContext;
if (sPendingFocusedBrowserSwitchingData.isSome()) {
@@ -1173,6 +1182,14 @@ void IMEStateManager::OnFocusInEditor(nsPresContext& aPresContext,
&aPresContext, TrueOrFalse(CanHandleWith(&aPresContext)), aElement,
&aEditorBase, sFocusedPresContext.get(), sFocusedElement.get(),
sActiveIMEContentObserver.get()));
+ if (aElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" aElement: %s", ToString(*aElement).c_str()));
+ }
+ if (sFocusedElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" sFocusedElement: %s", ToString(*sFocusedElement).c_str()));
+ }
if (!IsFocusedElement(aPresContext, aElement)) {
MOZ_LOG(sISMLog, LogLevel::Debug,
@@ -1266,9 +1283,15 @@ void IMEStateManager::OnReFocus(nsPresContext& aPresContext,
Element& aElement) {
MOZ_LOG(sISMLog, LogLevel::Info,
("OnReFocus(aPresContext=0x%p (available: %s), aElement=0x%p), "
- "sActiveIMEContentObserver=0x%p, aElement=0x%p",
+ "sActiveIMEContentObserver=0x%p, sFocusedElement=0x%p",
&aPresContext, TrueOrFalse(CanHandleWith(&aPresContext)), &aElement,
sActiveIMEContentObserver.get(), sFocusedElement.get()));
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" aElement: %s", ToString(aElement).c_str()));
+ if (sFocusedElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" sFocusedElement: %s", ToString(*sFocusedElement).c_str()));
+ }
if (NS_WARN_IF(!sTextInputHandlingWidget) ||
NS_WARN_IF(sTextInputHandlingWidget->Destroyed())) {
@@ -1306,10 +1329,44 @@ void IMEStateManager::OnReFocus(nsPresContext& aPresContext,
}
}
- InputContextAction action(InputContextAction::CAUSE_UNKNOWN,
- InputContextAction::FOCUS_NOT_CHANGED);
- IMEState newState = GetNewIMEState(aPresContext, &aElement);
- MOZ_ASSERT(newState.IsEditable());
+ 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()) {
+ nsTextControlFrame* const boundFrame =
+ textControlElement->GetTextControlState()->GetBoundFrame();
+ MOZ_ASSERT(!boundFrame);
+ MOZ_LOG(
+ sISMLog, LogLevel::Warning,
+ (" OnReFocus(), Temporarily disabling IME for the focused element "
+ "because probably the TextControlState could not return "
+ "TextEditor (textControlFrame: %p, textEditor: %p)",
+ boundFrame,
+ textControlElement->GetTextControlState()->GetExtantTextEditor()));
+ }
+ } else {
+ HTMLEditor* const htmlEditor =
+ nsContentUtils::GetHTMLEditor(&aPresContext);
+#ifdef DEBUG
+ MOZ_ASSERT(htmlEditor);
+ IMEState state;
+ MOZ_ASSERT(NS_SUCCEEDED(htmlEditor->GetPreferredIMEState(&state)));
+ MOZ_ASSERT(!state.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);
}
@@ -1406,6 +1463,14 @@ void IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
TrueOrFalse(sTextInputHandlingWidget &&
!sTextInputHandlingWidget->Destroyed()),
sActiveIMEContentObserver.get(), TrueOrFalse(sIsGettingNewIMEState)));
+ if (aElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" aElement: %s", ToString(*aElement).c_str()));
+ }
+ if (sFocusedElement) {
+ MOZ_LOG(sISMLog, LogLevel::Debug,
+ (" sFocusedElement: %s", ToString(*sFocusedElement).c_str()));
+ }
if (sIsGettingNewIMEState) {
MOZ_LOG(sISMLog, LogLevel::Debug,
diff --git a/dom/html/TextControlState.h b/dom/html/TextControlState.h
@@ -225,6 +225,9 @@ class TextControlState final : public SupportsWeakPtr {
nsFrameSelection* GetIndependentFrameSelection() const;
nsresult BindToFrame(nsTextControlFrame* aFrame);
MOZ_CAN_RUN_SCRIPT void UnbindFromFrame(nsTextControlFrame* aFrame);
+ [[nodiscard]] nsTextControlFrame* GetBoundFrame() const {
+ return mBoundFrame;
+ }
MOZ_CAN_RUN_SCRIPT nsresult PrepareEditor(const nsAString* aValue = nullptr);
void InitializeKeyboardEventListeners();