commit a49dc3702d34a5175014fb5ea642ffe5588c5fbd
parent c20c1d810978ac4c7240a88a11fd212396c80f1a
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date: Mon, 1 Dec 2025 23:03:13 +0000
Bug 2001825 - Make `EditorBase::GetPreferredIMEState()` return `Result<IMEState, nsresult>` r=m_kato
For making the implementation is for which editor, the method should be
a pure virtual function and `TextEditor` and `HTMLEditor` implement it
as they needed.
Additionally, it should return `Result` to avoid the caller to use
fallback IME state even temporarily.
Differential Revision: https://phabricator.services.mozilla.com/D274386
Diffstat:
9 files changed, 85 insertions(+), 90 deletions(-)
diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp
@@ -212,9 +212,8 @@ nsIContent::IMEState nsIContent::GetDesiredIMEState() {
if (!htmlEditor) {
return IMEState(IMEEnabled::Disabled);
}
- IMEState state;
- htmlEditor->GetPreferredIMEState(&state);
- return state;
+ // FYI: HTMLEditor::GetPreferredIMEState() is infallible.
+ return htmlEditor->GetPreferredIMEState().unwrap();
}
bool nsIContent::HasIndependentSelection() const {
diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp
@@ -1356,9 +1356,10 @@ void IMEStateManager::OnReFocus(nsPresContext& aPresContext,
nsContentUtils::GetHTMLEditor(&aPresContext);
#ifdef DEBUG
MOZ_ASSERT(htmlEditor);
- IMEState state;
- MOZ_ASSERT(NS_SUCCEEDED(htmlEditor->GetPreferredIMEState(&state)));
- MOZ_ASSERT(!state.IsEditable());
+ 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, "
diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp
@@ -856,14 +856,20 @@ void nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
IMEStateManager::GetActiveContentObserver();
if (observer && observer->IsObserving(*presContext, this)) {
if (const RefPtr<EditorBase> editorBase = GetExtantEditor()) {
- IMEState newState;
- editorBase->GetPreferredIMEState(&newState);
- OwningNonNull<nsGenericHTMLElement> kungFuDeathGrip(*this);
- IMEStateManager::UpdateIMEState(
- newState, kungFuDeathGrip, *editorBase,
- {IMEStateManager::UpdateIMEStateOption::ForceUpdate,
- IMEStateManager::UpdateIMEStateOption::
- DontCommitComposition});
+ // If the TextControlState does not have a bound frame,
+ // TextEditor::GetPreferredIMEState() may fail. In such case,
+ // TextEditor will update IME state when it's (re)initialized
+ // later.
+ Result<IMEState, nsresult> newStateOrError =
+ editorBase->GetPreferredIMEState();
+ if (MOZ_LIKELY(newStateOrError.isOk())) {
+ OwningNonNull<nsGenericHTMLElement> kungFuDeathGrip(*this);
+ IMEStateManager::UpdateIMEState(
+ newStateOrError.unwrap(), kungFuDeathGrip, *editorBase,
+ {IMEStateManager::UpdateIMEStateOption::ForceUpdate,
+ IMEStateManager::UpdateIMEStateOption::
+ DontCommitComposition});
+ }
}
}
}
@@ -2541,12 +2547,11 @@ nsIContent::IMEState nsGenericHTMLFormControlElement::GetDesiredIMEState() {
if (!textEditor) {
return nsGenericHTMLFormElement::GetDesiredIMEState();
}
- IMEState state;
- nsresult rv = textEditor->GetPreferredIMEState(&state);
- if (NS_FAILED(rv)) {
+ Result<IMEState, nsresult> stateOrError = textEditor->GetPreferredIMEState();
+ if (MOZ_UNLIKELY(stateOrError.isErr())) {
return nsGenericHTMLFormElement::GetDesiredIMEState();
}
- return state;
+ return stateOrError.unwrap();
}
void nsGenericHTMLFormControlElement::GetAutocapitalize(
diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp
@@ -466,13 +466,13 @@ nsresult EditorBase::PostCreateInternal() {
NS_SUCCEEDED(rv),
"EditorBase::FlushPendingSpellCheck() failed, but ignored");
- IMEState newState;
- rv = GetPreferredIMEState(&newState);
- if (NS_FAILED(rv)) {
+ Result<IMEState, nsresult> newStateOrError = GetPreferredIMEState();
+ if (MOZ_UNLIKELY(newStateOrError.isErr())) {
NS_WARNING("EditorBase::GetPreferredIMEState() failed");
return NS_OK;
}
- IMEStateManager::UpdateIMEState(newState, focusedElement, *this);
+ IMEStateManager::UpdateIMEState(newStateOrError.unwrap(), focusedElement,
+ *this);
}
// FYI: This call might cause destroying this editor.
@@ -720,15 +720,14 @@ NS_IMETHODIMP EditorBase::SetFlags(uint32_t aFlags) {
// Might be changing editable state, so, we need to reset current IME state
// if we're focused and the flag change causes IME state change.
if (RefPtr<Element> focusedElement = GetFocusedElement()) {
- IMEState newState;
- nsresult rv = GetPreferredIMEState(&newState);
- NS_WARNING_ASSERTION(
- NS_SUCCEEDED(rv),
- "EditorBase::GetPreferredIMEState() failed, but ignored");
- if (NS_SUCCEEDED(rv)) {
+ Result<IMEState, nsresult> newStateOrError = GetPreferredIMEState();
+ NS_WARNING_ASSERTION(newStateOrError.isOk(),
+ "EditorBase::GetPreferredIMEState() failed");
+ if (MOZ_LIKELY(newStateOrError.isOk())) {
// NOTE: When the enabled state isn't going to be modified, this method
// is going to do nothing.
- IMEStateManager::UpdateIMEState(newState, focusedElement, *this);
+ IMEStateManager::UpdateIMEState(newStateOrError.unwrap(), focusedElement,
+ *this);
}
}
@@ -3018,52 +3017,6 @@ nsresult EditorBase::CommitComposition() {
return rv;
}
-nsresult EditorBase::GetPreferredIMEState(IMEState* aState) {
- if (NS_WARN_IF(!aState)) {
- return NS_ERROR_INVALID_ARG;
- }
-
- aState->mEnabled = IMEEnabled::Enabled;
- aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
-
- if (IsReadonly()) {
- aState->mEnabled = IMEEnabled::Disabled;
- return NS_OK;
- }
-
- Element* rootElement = GetRoot();
- if (NS_WARN_IF(!rootElement)) {
- return NS_ERROR_FAILURE;
- }
-
- nsIFrame* frameForRootElement = rootElement->GetPrimaryFrame();
- if (NS_WARN_IF(!frameForRootElement)) {
- return NS_ERROR_FAILURE;
- }
-
- switch (frameForRootElement->StyleUIReset()->mIMEMode) {
- case StyleImeMode::Auto:
- if (IsPasswordEditor()) {
- aState->mEnabled = IMEEnabled::Password;
- }
- break;
- case StyleImeMode::Disabled:
- // we should use password state for |ime-mode: disabled;|.
- aState->mEnabled = IMEEnabled::Password;
- break;
- case StyleImeMode::Active:
- aState->mOpen = IMEState::OPEN;
- break;
- case StyleImeMode::Inactive:
- aState->mOpen = IMEState::CLOSED;
- break;
- case StyleImeMode::Normal:
- break;
- }
-
- return NS_OK;
-}
-
NS_IMETHODIMP EditorBase::GetComposing(bool* aResult) {
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h
@@ -263,7 +263,8 @@ class EditorBase : public nsIEditor,
/**
* Get preferred IME status of current widget.
*/
- virtual nsresult GetPreferredIMEState(widget::IMEState* aState);
+ [[nodiscard]] virtual Result<widget::IMEState, nsresult>
+ GetPreferredIMEState() const = 0;
/**
* Returns true if there is composition string and not fixed.
diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp
@@ -714,10 +714,9 @@ nsresult HTMLEditor::FocusedElementOrDocumentBecomesEditable(
}
// Although editor is already initialized due to re-used, ISM may not
// create IME content observer yet. So we have to create it.
- IMEState newState;
- nsresult rv = GetPreferredIMEState(&newState);
- if (NS_FAILED(rv)) {
- NS_WARNING("EditorBase::GetPreferredIMEState() failed");
+ Result<IMEState, nsresult> newStateOrError = GetPreferredIMEState();
+ if (MOZ_UNLIKELY(newStateOrError.isErr())) {
+ NS_WARNING("HTMLEditor::GetPreferredIMEState() failed");
mIsInDesignMode = false;
return NS_OK;
}
@@ -736,7 +735,8 @@ nsresult HTMLEditor::FocusedElementOrDocumentBecomesEditable(
mHasFocus = false;
mIsInDesignMode = false;
}
- IMEStateManager::UpdateIMEState(newState, focusedElement, *this);
+ IMEStateManager::UpdateIMEState(newStateOrError.unwrap(), focusedElement,
+ *this);
// XXX Do we need to notify focused TextEditor of focus? In theory,
// the TextEditor should get focus event in this case.
}
@@ -7417,15 +7417,10 @@ bool HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const {
return IsActiveInDOMWindow();
}
-nsresult HTMLEditor::GetPreferredIMEState(IMEState* aState) {
+Result<widget::IMEState, nsresult> HTMLEditor::GetPreferredIMEState() const {
// HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
- aState->mOpen = IMEState::DONT_CHANGE_OPEN_STATE;
- if (IsReadonly()) {
- aState->mEnabled = IMEEnabled::Disabled;
- } else {
- aState->mEnabled = IMEEnabled::Enabled;
- }
- return NS_OK;
+ return IMEState{IsReadonly() ? IMEEnabled::Disabled : IMEEnabled::Enabled,
+ IMEState::DONT_CHANGE_OPEN_STATE};
}
already_AddRefed<Element> HTMLEditor::GetInputEventTargetElement() const {
diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h
@@ -203,7 +203,8 @@ class HTMLEditor final : public EditorBase,
dom::EventTarget* GetDOMEventTarget() const final;
[[nodiscard]] Element* FindSelectionRoot(const nsINode& aNode) const final;
bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const final;
- nsresult GetPreferredIMEState(widget::IMEState* aState) final;
+ [[nodiscard]] Result<widget::IMEState, nsresult> GetPreferredIMEState()
+ const final;
MOZ_CAN_RUN_SCRIPT nsresult
OnFocus(const nsINode& aOriginalEventTargetNode) final;
nsresult OnBlur(const dom::EventTarget* aEventTarget) final;
diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp
@@ -308,6 +308,43 @@ UniquePtr<PasswordMaskData> TextEditor::PreDestroy() {
return passwordMaskData;
}
+Result<widget::IMEState, nsresult> TextEditor::GetPreferredIMEState() const {
+ using IMEState = widget::IMEState;
+ using IMEEnabled = widget::IMEEnabled;
+
+ if (IsReadonly()) {
+ return IMEState{IMEEnabled::Disabled, IMEState::DONT_CHANGE_OPEN_STATE};
+ }
+
+ Element* const textControlElement = GetExposedRoot();
+ if (NS_WARN_IF(!textControlElement)) {
+ return Err(NS_ERROR_FAILURE);
+ }
+ MOZ_ASSERT(textControlElement->IsTextControlElement());
+
+ nsIFrame* const textControlFrame = textControlElement->GetPrimaryFrame();
+ if (NS_WARN_IF(!textControlFrame)) {
+ return Err(NS_ERROR_FAILURE);
+ }
+
+ switch (textControlFrame->StyleUIReset()->mIMEMode) {
+ case StyleImeMode::Auto:
+ default:
+ return IMEState{
+ IsPasswordEditor() ? IMEEnabled::Password : IMEEnabled::Enabled,
+ IMEState::DONT_CHANGE_OPEN_STATE};
+ case StyleImeMode::Disabled:
+ // we should use password state for |ime-mode: disabled;|.
+ return IMEState{IMEEnabled::Password, IMEState::DONT_CHANGE_OPEN_STATE};
+ case StyleImeMode::Active:
+ return IMEState{IMEEnabled::Enabled, IMEState::OPEN};
+ case StyleImeMode::Inactive:
+ return IMEState{IMEEnabled::Enabled, IMEState::CLOSED};
+ case StyleImeMode::Normal:
+ return IMEState{IMEEnabled::Enabled, IMEState::DONT_CHANGE_OPEN_STATE};
+ }
+}
+
nsresult TextEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) {
// NOTE: When you change this method, you should also change:
// * editor/libeditor/tests/test_texteditor_keyevent_handling.html
diff --git a/editor/libeditor/TextEditor.h b/editor/libeditor/TextEditor.h
@@ -149,6 +149,9 @@ class TextEditor final : public EditorBase,
nsresult OnBlur(const dom::EventTarget* aEventTarget) final;
+ [[nodiscard]] Result<widget::IMEState, nsresult> GetPreferredIMEState()
+ const final;
+
/**
* The maximum number of characters allowed.
* default: -1 (unlimited).