diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 66b41cc80505..1442ef2b8cd8 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -2088,14 +2088,19 @@ NS_IMETHODIMP HTMLEditor::RebuildDocumentFromSource( NS_IMETHODIMP HTMLEditor::InsertElementAtSelection(Element* aElement, bool aDeleteSelection) { - nsresult rv = InsertElementAtSelectionAsAction(aElement, aDeleteSelection); + InsertElementOptions options; + if (aDeleteSelection) { + options += InsertElementOption::DeleteSelection; + } + nsresult rv = InsertElementAtSelectionAsAction(aElement, options); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertElementAtSelectionAsAction() failed"); return rv; } nsresult HTMLEditor::InsertElementAtSelectionAsAction( - Element* aElement, bool aDeleteSelection, nsIPrincipal* aPrincipal) { + Element* aElement, const InsertElementOptions aOptions, + nsIPrincipal* aPrincipal) { if (NS_WARN_IF(!aElement)) { return NS_ERROR_INVALID_ARG; } @@ -2175,14 +2180,19 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction( } } - if (aDeleteSelection) { + if (aOptions.contains(InsertElementOption::DeleteSelection) && + !SelectionRef().IsCollapsed()) { if (!HTMLEditUtils::IsBlockElement( *aElement, BlockInlineCheck::UseComputedDisplayOutsideStyle)) { // E.g., inserting an image. In this case we don't need to delete any // inline wrappers before we do the insertion. Otherwise we let // DeleteSelectionAndPrepareToCreateNode do the deletion for us, which // calls DeleteSelection with aStripWrappers = eStrip. - nsresult rv = DeleteSelectionAsSubAction(eNone, eNoStrip); + nsresult rv = DeleteSelectionAsSubAction( + eNone, + aOptions.contains(InsertElementOption::SplitAncestorInlineElements) + ? eStrip + : eNoStrip); if (NS_FAILED(rv)) { NS_WARNING( "EditorBase::DeleteSelectionAsSubAction(eNone, eNoStrip) failed"); @@ -2235,6 +2245,28 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction( return NS_ERROR_FAILURE; } + if (aOptions.contains(InsertElementOption::SplitAncestorInlineElements)) { + if (const RefPtr topmostInlineElement = Element::FromNodeOrNull( + HTMLEditUtils::GetMostDistantAncestorInlineElement( + *pointToInsert.ContainerAs(), + BlockInlineCheck::UseComputedDisplayOutsideStyle, + editingHost))) { + Result splitInlinesResult = + SplitNodeDeepWithTransaction( + *topmostInlineElement, pointToInsert, + SplitAtEdges::eDoNotCreateEmptyContainer); + if (MOZ_UNLIKELY(splitInlinesResult.isErr())) { + NS_WARNING("HTMLEditor::SplitNodeDeepWithTransaction() failed"); + return splitInlinesResult.unwrapErr(); + } + splitInlinesResult.inspect().IgnoreCaretPointSuggestion(); + auto splitPoint = + splitInlinesResult.inspect().AtSplitPoint(); + if (MOZ_LIKELY(splitPoint.IsSet())) { + pointToInsert = std::move(splitPoint); + } + } + } { Result insertElementResult = InsertNodeIntoProperAncestorWithTransaction( diff --git a/editor/libeditor/HTMLEditor.h b/editor/libeditor/HTMLEditor.h index 3ab536eee171..2dd8aaa1922d 100644 --- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -266,9 +266,17 @@ class HTMLEditor final : public EditorBase, [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertParagraphSeparatorAsAction(nsIPrincipal* aPrincipal = nullptr); - MOZ_CAN_RUN_SCRIPT nsresult - InsertElementAtSelectionAsAction(Element* aElement, bool aDeleteSelection, - nsIPrincipal* aPrincipal = nullptr); + enum class InsertElementOption { + // Delete selection if set, otherwise, insert aElement at start or end of + // selection. + DeleteSelection, + // Whether split all inline ancestors or not. + SplitAncestorInlineElements, + }; + using InsertElementOptions = EnumSet; + MOZ_CAN_RUN_SCRIPT nsresult InsertElementAtSelectionAsAction( + Element* aElement, const InsertElementOptions aOptions, + nsIPrincipal* aPrincipal = nullptr); MOZ_CAN_RUN_SCRIPT nsresult InsertLinkAroundSelectionAsAction( Element* aAnchorElement, nsIPrincipal* aPrincipal = nullptr); diff --git a/editor/libeditor/HTMLEditorCommands.cpp b/editor/libeditor/HTMLEditorCommands.cpp index ecccc7c491cf..3a924a392fce 100644 --- a/editor/libeditor/HTMLEditorCommands.cpp +++ b/editor/libeditor/HTMLEditorCommands.cpp @@ -1234,9 +1234,18 @@ nsresult InsertTagCommand::DoCommand(Command aCommand, EditorBase& aEditorBase, if (NS_WARN_IF(!newElement)) { return NS_ERROR_FAILURE; } + HTMLEditor::InsertElementOptions options{ + HTMLEditor::InsertElementOption::DeleteSelection}; + // We did insert without splitting ancestor inline elements, but the + // other browsers split them. Therefore, let's do it only when the document + // is content. + if (tagName == nsGkAtoms::img && + htmlEditor->GetDocument()->IsContentDocument()) { + options += HTMLEditor::InsertElementOption::SplitAncestorInlineElements; + } nsresult rv = MOZ_KnownLive(htmlEditor) - ->InsertElementAtSelectionAsAction(newElement, true, aPrincipal); + ->InsertElementAtSelectionAsAction(newElement, options, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertElementAtSelectionAsAction() failed"); return rv; @@ -1297,9 +1306,17 @@ nsresult InsertTagCommand::DoCommandParam(Command aCommand, return rv; } + HTMLEditor::InsertElementOptions options{ + HTMLEditor::InsertElementOption::DeleteSelection}; + // We did insert without splitting ancestor inline elements, but the + // other browsers split them. Therefore, let's do it only when the document + // is content. + if (htmlEditor->GetDocument()->IsContentDocument()) { + options += HTMLEditor::InsertElementOption::SplitAncestorInlineElements; + } nsresult rv = MOZ_KnownLive(htmlEditor) - ->InsertElementAtSelectionAsAction(newElement, true, aPrincipal); + ->InsertElementAtSelectionAsAction(newElement, options, aPrincipal); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::InsertElementAtSelectionAsAction() failed"); return rv; diff --git a/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js b/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js index e0a4cc0fb0c0..5f0ada261a6d 100644 --- a/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js +++ b/editor/libeditor/tests/browserscope/lib/richtext2/currentStatus.js @@ -266,6 +266,9 @@ const knownFailures = { "I-Proposed-IIMG:._IMG-1_SO-dM": true, "I-Proposed-IIMG:._IMG-1_SO-body": true, "I-Proposed-IIMG:._IMG-1_SO-div": true, + "I-Proposed-IIMG:url_IMG-1_SO-dM": true, + "I-Proposed-IIMG:url_IMG-1_SO-body": true, + "I-Proposed-IIMG:url_IMG-1_SO-div": true, "Q-Proposed-CONTENTREADONLY_TEXT-1-dM": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false), "Q-Proposed-CONTENTREADONLY_TEXT-1-body": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false), "Q-Proposed-CONTENTREADONLY_TEXT-1-div": !SpecialPowers.getBoolPref("dom.document.edit_command.contentReadOnly.enabled", false), @@ -867,6 +870,9 @@ const knownFailures = { "I-Proposed-IIMG:._IMG-1_SO-dM": true, "I-Proposed-IIMG:._IMG-1_SO-body": true, "I-Proposed-IIMG:._IMG-1_SO-div": true, + "I-Proposed-IIMG:url_IMG-1_SO-dM": true, + "I-Proposed-IIMG:url_IMG-1_SO-body": true, + "I-Proposed-IIMG:url_IMG-1_SO-div": true, "I-Proposed-IHTML:BR_TEXT-1_SC-dM": true, "I-Proposed-IHTML:BR_TEXT-1_SC-body": true, "I-Proposed-IHTML:BR_TEXT-1_SC-div": true, diff --git a/testing/web-platform/meta/editing/run/insertimage.html.ini b/testing/web-platform/meta/editing/run/insertimage.html.ini index 4ad0d7dd8fa1..740e8bf9057c 100644 --- a/testing/web-platform/meta/editing/run/insertimage.html.ini +++ b/testing/web-platform/meta/editing/run/insertimage.html.ini @@ -1,15 +1,6 @@ [insertimage.html] expected: if (os == "android") and fission: [OK, TIMEOUT] - [[["insertimage","/img/lion.svg"\]\] "foo{bar}baz" compare innerHTML] - expected: FAIL - - [[["insertimage","/img/lion.svg"\]\] "foo{bar}baz" compare innerHTML] - expected: FAIL - - [[["insertimage","/img/lion.svg"\]\] "foo{bar}baz" compare innerHTML] - expected: FAIL - [[["stylewithcss","true"\],["defaultparagraphseparator","div"\],["insertimage","/img/lion.svg"\]\] "

foo[bar

baz\]quz" compare innerHTML] expected: FAIL diff --git a/testing/web-platform/tests/editing/data/insertimage.js b/testing/web-platform/tests/editing/data/insertimage.js index b18388a7971d..b62331e15209 100644 --- a/testing/web-platform/tests/editing/data/insertimage.js +++ b/testing/web-platform/tests/editing/data/insertimage.js @@ -12,7 +12,7 @@ var browserTests = [ {"insertimage":[false,false,"",false,false,""]}], ["foo[]bar", [["insertimage","/img/lion.svg"]], - "foo{}bar", + "foobar", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo[bar]baz", @@ -27,17 +27,17 @@ var browserTests = [ {"insertimage":[false,false,"",false,false,""]}], ["foo[bar]baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{bar}baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{bar}baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["[foobar]baz", @@ -62,57 +62,57 @@ var browserTests = [ {"stylewithcss":[false,true,"",false,false,""],"insertimage":[false,false,"",false,false,""]}], ["foo[barbaz]", [["insertimage","/img/lion.svg"]], - "foo{}", + "foo", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{barbaz}", [["insertimage","/img/lion.svg"]], - "foo{}", + "foo", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo[barbaz]quz", [["stylewithcss","true"],["insertimage","/img/lion.svg"]], - "foo{}quz", + "fooquz", [true,true], {"stylewithcss":[false,false,"",false,true,""],"insertimage":[false,false,"",false,false,""]}], ["foo[barbaz]quz", [["stylewithcss","false"],["insertimage","/img/lion.svg"]], - "foo{}quz", + "fooquz", [true,true], {"stylewithcss":[false,true,"",false,false,""],"insertimage":[false,false,"",false,false,""]}], ["foo[bar]baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{bar}baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{bar}baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo[bar]baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{bar}baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo{bar}baz", [["insertimage","/img/lion.svg"]], - "foo{}baz", + "foobaz", [true], {"insertimage":[false,false,"",false,false,""]}], ["foo[barbaz]quz", [["insertimage","/img/lion.svg"]], - "foo{}quz", + "fooquz", [true], {"insertimage":[false,false,"",false,false,""]}], ["

foo

[bar]

baz

", @@ -355,4 +355,32 @@ var browserTests = [ "foo{}bar", [true], {"insertimage":[false,false,"",false,false,""]}], +["
{}
", + [["insertimage","/img/lion.svg"]], + ["
", + "

"], + [true], + {}], +["
{}
", + [["insertimage","/img/lion.svg"]], + ["
", + "

"], + [true], + {}], +["
{}
", + [["insertimage","/img/lion.svg"]], + ["
", + "

"], + [true], + {}], +["
A[]B
", + [["insertimage","/img/lion.svg"]], + "
AB
", + [true], + {}], +["
A[]B
", + [["insertimage","/img/lion.svg"]], + "
AB
", + [true], + {}], ]