tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mdom/base/FragmentOrElement.cpp | 5++---
Mdom/events/IMEStateManager.cpp | 7++++---
Mdom/html/nsGenericHTMLElement.cpp | 29+++++++++++++++++------------
Meditor/libeditor/EditorBase.cpp | 67++++++++++---------------------------------------------------------
Meditor/libeditor/EditorBase.h | 3++-
Meditor/libeditor/HTMLEditor.cpp | 21++++++++-------------
Meditor/libeditor/HTMLEditor.h | 3++-
Meditor/libeditor/TextEditor.cpp | 37+++++++++++++++++++++++++++++++++++++
Meditor/libeditor/TextEditor.h | 3+++
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).