diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp
index 7df0047f9546..0e5bf6abcd86 100644
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -33,6 +33,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
#include "mozilla/RangeUtils.h"
#include "mozilla/StaticPrefs_editor.h" // for StaticPrefs::editor_*
#include "mozilla/TextComposition.h"
@@ -223,6 +224,19 @@ void HTMLEditor::OnStartToHandleTopLevelEditSubAction(
!aRv.Failed(),
"EditorBase::OnStartToHandleTopLevelEditSubAction() failed");
+ // Let's work with the latest layout information after (maybe) dispatching
+ // `beforeinput` event.
+ RefPtr document = GetDocument();
+ if (NS_WARN_IF(!document)) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return;
+ }
+ document->FlushPendingNotifications(FlushType::Frames);
+ if (NS_WARN_IF(Destroyed())) {
+ aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
+ return;
+ }
+
// Remember where our selection was before edit action took place:
const auto atCompositionStart =
GetFirstIMESelectionStartPoint();
@@ -288,11 +302,6 @@ void HTMLEditor::OnStartToHandleTopLevelEditSubAction(
}
// Stabilize the document against contenteditable count changes
- Document* document = GetDocument();
- if (NS_WARN_IF(!document)) {
- aRv.Throw(NS_ERROR_FAILURE);
- return;
- }
if (document->GetEditingState() == Document::EditingState::eContentEditable) {
document->ChangeContentEditableCount(nullptr, +1);
TopLevelEditSubActionDataRef().mRestoreContentEditableCount = true;
diff --git a/editor/libeditor/HTMLEditUtils.cpp b/editor/libeditor/HTMLEditUtils.cpp
index 993ab283ba5b..edcf3672d5b3 100644
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -1256,6 +1256,25 @@ bool HTMLEditUtils::CanNodeContain(nsHTMLTag aParentTagId,
return !!(parent.mCanContainGroups & child.mGroup);
}
+bool HTMLEditUtils::ContentIsInert(const nsIContent& aContent) {
+ for (nsIContent* content :
+ aContent.InclusiveFlatTreeAncestorsOfType()) {
+ if (nsIFrame* frame = content->GetPrimaryFrame()) {
+ return frame->StyleUI()->IsInert();
+ }
+ // If it doesn't have primary frame, we need to check its ancestors.
+ // This may occur if it's an invisible text node or element nodes whose
+ // display is an invisible value.
+ if (!content->IsElement()) {
+ continue;
+ }
+ if (content->AsElement()->State().HasState(dom::ElementState::INERT)) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool HTMLEditUtils::IsContainerNode(nsHTMLTag aTagId) {
NS_ASSERTION(aTagId > eHTMLTag_unknown && aTagId <= eHTMLTag_userdefined,
"aTagId out of range!");
diff --git a/editor/libeditor/HTMLEditUtils.h b/editor/libeditor/HTMLEditUtils.h
index f02c67b4c418..3a9f899a5adb 100644
--- a/editor/libeditor/HTMLEditUtils.h
+++ b/editor/libeditor/HTMLEditUtils.h
@@ -77,15 +77,7 @@ class HTMLEditUtils final {
/**
* Return true if inclusive flat tree ancestor has `inert` state.
*/
- static bool ContentIsInert(const nsIContent& aContent) {
- for (const Element* element :
- aContent.InclusiveFlatTreeAncestorsOfType()) {
- if (element->State().HasState(dom::ElementState::INERT)) {
- return true;
- }
- }
- return false;
- }
+ static bool ContentIsInert(const nsIContent& aContent);
/**
* IsNeverContentEditableElementByUser() returns true if the element's content
diff --git a/testing/web-platform/tests/inert/inert-inlines-around-selection-range-in-contenteditable.html b/testing/web-platform/tests/inert/inert-inlines-around-selection-range-in-contenteditable.html
index ff00e16a7a43..c22c798084dc 100644
--- a/testing/web-platform/tests/inert/inert-inlines-around-selection-range-in-contenteditable.html
+++ b/testing/web-platform/tests/inert/inert-inlines-around-selection-range-in-contenteditable.html
@@ -85,6 +85,102 @@ document.addEventListener("DOMContentLoaded", () => {
const desc = `execCommand("delete") at ${t.name}`
assert_equals(editingHost.innerHTML, "af", `${desc}: should be deleted`);
}, "a[bcXYZde]f");
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `a[bcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `{abcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `a[bcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `{abcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `a[bcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `{abcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `a[bcde]f`);
+
+ test(t => {
+ utils.setupEditingHost(t.name, { selection: "setBaseAndExtent" });
+ const initialInnerHTML = editingHost.innerHTML;
+ document.execCommand("delete");
+ const desc = `execCommand("delete") at ${t.name}`
+ assert_equals(
+ editingHost.innerHTML,
+ initialInnerHTML,
+ `${desc}: content should not be deleted because anchor node of Selection is in the `
+ );
+ }, `{abcde]f`);
});