From b9ef2f71b3ad694dfb66ae990e10482955dae7bd Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 27 Aug 2024 00:39:49 +0000 Subject: [PATCH] Bug 1911010 - Make `IMEContentObserver` observe `ParentChainChanged` and let `IMEStateManager` know that r=smaug `mozAutoDocUpdate` does not make it in a document change when container node of `insertBefore` has already been removed from the tree. Therefore, once `IMEContentObserver::mRootElement` is removed from the DOM tree without a focus move, `IMEContentObserver` is notified mutations not in a document change. Similar situations are handled in `IMEStateManager::OnRemoveContent` with emulating a focus change and that will destroy the active `IMEContentObserver`. Therefore, if `IMEContentObserver::mRootElement` is removed, we should emulate a focus move when `IMEStateManager` does not have focused element but there is active `IMEContentObserver` (that means it is/was in the design mode). However, checking whether the removed node contains the observing node of the active `IMEContentObserver` may be expensive. So, doing expensive things in `IMEStateManager::OnRemoveContent` may make mutations slower. Therefore, this patch makes `IMEContentObserver` observe `ParentChainChanged` and it let `IMEStateManager` know that with calling its `OnParentChainChangedOfObservingElement`. Finally, it calls `IMEStateManager::OnRemoveContent` to emulate "blur" (and refocus if it's required). Differential Revision: https://phabricator.services.mozilla.com/D218696 --- dom/base/FragmentOrElement.cpp | 2 +- dom/base/nsINode.cpp | 77 +++++--- dom/events/IMEContentObserver.cpp | 41 +++++ dom/events/IMEContentObserver.h | 2 + dom/events/IMEStateManager.cpp | 53 +++++- dom/events/IMEStateManager.h | 7 + dom/events/TextComposition.cpp | 8 +- editor/libeditor/HTMLEditor.cpp | 39 ++-- ...body_in_design_mode_during_focus_move.html | 37 ++++ widget/tests/browser/browser.toml | 3 + ...y_removed_and_reconnected_in_designMode.js | 168 ++++++++++++++++++ 11 files changed, 386 insertions(+), 51 deletions(-) create mode 100644 testing/web-platform/tests/editing/crashtests/remove_body_in_design_mode_during_focus_move.html create mode 100644 widget/tests/browser/browser_test_ime_state_after_body_removed_and_reconnected_in_designMode.js diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index b79a9971988e..6fb2ca6bbdf9 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -185,7 +185,7 @@ HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const { } nsIContent::IMEState nsIContent::GetDesiredIMEState() { - if (!IsEditable()) { + if (!IsEditable() || !IsInComposedDoc()) { // Check for the special case where we're dealing with elements which don't // have the editable flag set, but are readwrite (such as text controls). if (!IsElement() || diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 431288b3ee6d..f698b5921b4d 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -591,46 +591,73 @@ nsIContent* nsINode::GetSelectionRootContent(PresShell* aPresShell, bool aAllowCrossShadowBoundary) { NS_ENSURE_TRUE(aPresShell, nullptr); - if (IsDocument()) return AsDocument()->GetRootElement(); - if (!IsContent()) return nullptr; + const bool isContent = IsContent(); - if (GetComposedDoc() != aPresShell->GetDocument()) { + if (!isContent && !IsDocument()) { return nullptr; } - if (AsContent()->HasIndependentSelection() || IsInNativeAnonymousSubtree()) { - // This node should be an inclusive descendant of input/textarea editor. - // In that case, the anonymous
for TextEditor should be always the - // selection root. - // FIXME: If Selection for the document is collapsed in or - //