Backed out 7 changesets (bug 1716728) because dependent bugs were backed out.

Backed out changeset d302a501a181 (bug 1716728)
Backed out changeset 75bf5a053c5c (bug 1716728)
Backed out changeset 51e9d6b164e6 (bug 1716728)
Backed out changeset 2d1e67b5b3ee (bug 1716728)
Backed out changeset ccc371872958 (bug 1716728)
Backed out changeset 79501f3bd5e1 (bug 1716728)
Backed out changeset c1c1c8158e38 (bug 1716728)
This commit is contained in:
Ryan VanderMeulen 2021-06-17 11:01:21 -04:00
parent 8f9a37e741
commit ebdf8becb9
9 changed files with 273 additions and 173 deletions

View File

@ -205,6 +205,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaddingBRElementForEmptyEditor)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionController)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMEContentObserver)
@ -229,6 +230,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase)
return NS_SUCCESS_INTERRUPTED_TRAVERSE;
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaddingBRElementForEmptyEditor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver)
@ -361,14 +363,13 @@ nsresult EditorBase::InitEditorContentAndSelection() {
if (IsTextEditor()) {
MOZ_TRY(EnsureEmptyTextFirstChild());
} else {
nsresult rv = MOZ_KnownLive(AsHTMLEditor())
->MaybeCreatePaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING(
"HTMLEditor::MaybeCreatePaddingBRElementForEmptyEditor() failed");
return rv;
}
}
nsresult rv = MaybeCreatePaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING(
"EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() failed");
return rv;
}
// If the selection hasn't been set up yet, set it up collapsed to the end of
@ -642,6 +643,7 @@ void EditorBase::PreDestroy(bool aDestroyingFrames) {
mTextInputListener = nullptr;
mSpellcheckCheckboxState = eTriUnset;
mRootElement = nullptr;
mPaddingBRElementForEmptyEditor = nullptr;
// Transaction may grab this instance. Therefore, they should be released
// here for stopping the circular reference with this instance.
@ -1053,8 +1055,25 @@ nsresult EditorBase::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
DoAfterUndoTransaction();
}
if (IsHTMLEditor()) {
rv = AsHTMLEditor()->ReflectPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(!mRootElement)) {
NS_WARNING("Failed to handle padding BR Element due to no root element");
rv = NS_ERROR_FAILURE;
} else {
// The idea here is to see if the magic empty node has suddenly
// reappeared as the result of the undo. If it has, set our state
// so we remember it. There is a tradeoff between doing here and
// at redo, or doing it everywhere else that might care. Since undo
// and redo are relatively rare, it makes sense to take the (small)
// performance hit here.
nsIContent* firstLeafChild = HTMLEditUtils::GetFirstLeafContent(
*mRootElement, {LeafNodeType::OnlyLeafNode});
if (firstLeafChild &&
EditorUtils::IsPaddingBRElementForEmptyEditor(*firstLeafChild)) {
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(firstLeafChild);
} else {
mPaddingBRElementForEmptyEditor = nullptr;
}
}
}
@ -1118,8 +1137,26 @@ nsresult EditorBase::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
DoAfterRedoTransaction();
}
if (IsHTMLEditor()) {
rv = AsHTMLEditor()->ReflectPaddingBRElementForEmptyEditor();
if (NS_WARN_IF(!mRootElement)) {
NS_WARNING("Failed to handle padding BR element due to no root element");
rv = NS_ERROR_FAILURE;
} else {
// We may take empty <br> element for empty editor back with this redo.
// We need to store it again.
// XXX Looks like that this is too slow if there are a lot of nodes.
// Shouldn't we just scan children in the root?
nsCOMPtr<nsIHTMLCollection> nodeList =
mRootElement->GetElementsByTagName(u"br"_ns);
MOZ_ASSERT(nodeList);
Element* brElement =
nodeList->Length() == 1 ? nodeList->Item(0) : nullptr;
if (brElement &&
EditorUtils::IsPaddingBRElementForEmptyEditor(*brElement)) {
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(brElement);
} else {
mPaddingBRElementForEmptyEditor = nullptr;
}
}
}
@ -1468,7 +1505,7 @@ nsresult EditorBase::ComputeValueInternal(const nsAString& aFormatType,
!(aDocumentEncoderFlags & (nsIDocumentEncoder::OutputSelectionOnly |
nsIDocumentEncoder::OutputWrap))) {
// Shortcut for empty editor case.
if (IsEmpty()) {
if (mPaddingBRElementForEmptyEditor) {
aOutputString.Truncate();
return NS_OK;
}
@ -3547,6 +3584,110 @@ nsresult EditorBase::EnsurePaddingBRElementInMultilineEditor() {
return NS_OK;
}
nsresult EditorBase::EnsureNoPaddingBRElementForEmptyEditor() {
MOZ_ASSERT(IsEditActionDataAvailable());
if (!mPaddingBRElementForEmptyEditor) {
return NS_OK;
}
// If we're an HTML editor, a mutation event listener may recreate padding
// <br> element for empty editor again during the call of
// DeleteNodeWithTransaction(). So, move it first.
RefPtr<HTMLBRElement> paddingBRElement(
std::move(mPaddingBRElementForEmptyEditor));
nsresult rv = DeleteNodeWithTransaction(*paddingBRElement);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DeleteNodeWithTransaction() failed");
return rv;
}
nsresult EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() {
MOZ_ASSERT(IsEditActionDataAvailable());
if (mPaddingBRElementForEmptyEditor) {
return NS_OK;
}
IgnoredErrorResult ignoredError;
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eCreatePaddingBRElementForEmptyEditor,
nsIEditor::eNone, ignoredError);
if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
return ignoredError.StealNSResult();
}
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
"TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
ignoredError.SuppressException();
RefPtr<Element> rootElement = GetRoot();
if (!rootElement) {
return NS_OK;
}
// Now we've got the body element. Iterate over the body element's children,
// looking for editable content. If no editable content is found, insert the
// padding <br> element.
EditorType editorType = GetEditorType();
bool isRootEditable =
EditorUtils::IsEditableContent(*rootElement, editorType);
for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild;
rootChild = rootChild->GetNextSibling()) {
if (EditorUtils::IsPaddingBRElementForEmptyEditor(*rootChild) ||
!isRootEditable ||
EditorUtils::IsEditableContent(*rootChild, editorType) ||
HTMLEditUtils::IsBlockElement(*rootChild)) {
return NS_OK;
}
}
// Skip adding the padding <br> element for empty editor if body
// is read-only.
if (IsHTMLEditor() && !HTMLEditUtils::IsSimplyEditableNode(*rootElement)) {
return NS_OK;
}
// Create a br.
RefPtr<Element> newBRElement = CreateHTMLContent(nsGkAtoms::br);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(!newBRElement)) {
return NS_ERROR_FAILURE;
}
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(newBRElement.get());
// Give it a special attribute.
newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
// Put the node in the document.
nsresult rv =
InsertNodeWithTransaction(*newBRElement, EditorDOMPoint(rootElement, 0));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
}
// Set selection.
SelectionRef().CollapseInLimiter(EditorRawDOMPoint(rootElement, 0),
ignoredError);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Selection::CollapseInLimiter() failed, but ignored");
return NS_OK;
}
void EditorBase::BeginUpdateViewBatch() {
MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(mUpdateCount >= 0, "bad state");

View File

@ -1907,6 +1907,20 @@ class EditorBase : public nsIEditor,
uint32_t aOffset,
uint32_t aLength);
/**
* EnsureNoPaddingBRElementForEmptyEditor() removes padding <br> element
* for empty editor if there is.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureNoPaddingBRElementForEmptyEditor();
/**
* MaybeCreatePaddingBRElementForEmptyEditor() creates padding <br> element
* for empty editor if there is no children.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeCreatePaddingBRElementForEmptyEditor();
/**
* MarkElementDirty() sets a special dirty attribute on the element.
* Usually this will be called immediately after creating a new node.
@ -2807,6 +2821,10 @@ class EditorBase : public nsIEditor,
// Cached root node.
RefPtr<Element> mRootElement;
// mPaddingBRElementForEmptyEditor should be used for placing caret
// at proper position when editor is empty.
RefPtr<dom::HTMLBRElement> mPaddingBRElementForEmptyEditor;
// The form field as an event receiver.
nsCOMPtr<dom::EventTarget> mEventTarget;
RefPtr<EditorEventListener> mEventListener;

View File

@ -768,132 +768,6 @@ nsresult HTMLEditor::EnsureCaretNotAfterPaddingBRElement() {
return rv;
}
nsresult HTMLEditor::MaybeCreatePaddingBRElementForEmptyEditor() {
MOZ_ASSERT(IsEditActionDataAvailable());
if (mPaddingBRElementForEmptyEditor) {
return NS_OK;
}
IgnoredErrorResult ignoredError;
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eCreatePaddingBRElementForEmptyEditor,
nsIEditor::eNone, ignoredError);
if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
return ignoredError.StealNSResult();
}
NS_WARNING_ASSERTION(
!ignoredError.Failed(),
"TextEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
ignoredError.SuppressException();
RefPtr<Element> rootElement = GetRoot();
if (!rootElement) {
return NS_OK;
}
// Now we've got the body element. Iterate over the body element's children,
// looking for editable content. If no editable content is found, insert the
// padding <br> element.
EditorType editorType = GetEditorType();
bool isRootEditable =
EditorUtils::IsEditableContent(*rootElement, editorType);
for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild;
rootChild = rootChild->GetNextSibling()) {
if (EditorUtils::IsPaddingBRElementForEmptyEditor(*rootChild) ||
!isRootEditable ||
EditorUtils::IsEditableContent(*rootChild, editorType) ||
HTMLEditUtils::IsBlockElement(*rootChild)) {
return NS_OK;
}
}
// Skip adding the padding <br> element for empty editor if body
// is read-only.
if (IsHTMLEditor() && !HTMLEditUtils::IsSimplyEditableNode(*rootElement)) {
return NS_OK;
}
// Create a br.
RefPtr<Element> newBRElement = CreateHTMLContent(nsGkAtoms::br);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(!newBRElement)) {
return NS_ERROR_FAILURE;
}
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(newBRElement.get());
// Give it a special attribute.
newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
// Put the node in the document.
nsresult rv =
InsertNodeWithTransaction(*newBRElement, EditorDOMPoint(rootElement, 0));
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
return rv;
}
// Set selection.
SelectionRef().CollapseInLimiter(EditorRawDOMPoint(rootElement, 0),
ignoredError);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(!ignoredError.Failed(),
"Selection::CollapseInLimiter() failed, but ignored");
return NS_OK;
}
nsresult HTMLEditor::EnsureNoPaddingBRElementForEmptyEditor() {
MOZ_ASSERT(IsEditActionDataAvailable());
if (!mPaddingBRElementForEmptyEditor) {
return NS_OK;
}
// If we're an HTML editor, a mutation event listener may recreate padding
// <br> element for empty editor again during the call of
// DeleteNodeWithTransaction(). So, move it first.
RefPtr<HTMLBRElement> paddingBRElement(
std::move(mPaddingBRElementForEmptyEditor));
nsresult rv = DeleteNodeWithTransaction(*paddingBRElement);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::DeleteNodeWithTransaction() failed");
return rv;
}
nsresult HTMLEditor::ReflectPaddingBRElementForEmptyEditor() {
if (NS_WARN_IF(!mRootElement)) {
NS_WARNING("Failed to handle padding BR element due to no root element");
return NS_ERROR_FAILURE;
}
// The idea here is to see if the magic empty node has suddenly reappeared. If
// it has, set our state so we remember it. There is a tradeoff between doing
// here and at redo, or doing it everywhere else that might care. Since undo
// and redo are relatively rare, it makes sense to take the (small)
// performance hit here.
nsIContent* firstLeafChild = HTMLEditUtils::GetFirstLeafContent(
*mRootElement, {LeafNodeType::OnlyLeafNode});
if (firstLeafChild &&
EditorUtils::IsPaddingBRElementForEmptyEditor(*firstLeafChild)) {
mPaddingBRElementForEmptyEditor =
static_cast<HTMLBRElement*>(firstLeafChild);
} else {
mPaddingBRElementForEmptyEditor = nullptr;
}
return NS_OK;
}
nsresult HTMLEditor::PrepareInlineStylesForCaret() {
MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
MOZ_ASSERT(SelectionRef().IsCollapsed());

View File

@ -189,7 +189,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLEditor, EditorBase)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTypeInState)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mComposerCommandsUpdater)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChangedRangeForTopLevelEditSubAction)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaddingBRElementForEmptyEditor)
tmp->HideAnonymousEditingUIs();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -197,7 +196,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLEditor, EditorBase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTypeInState)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mComposerCommandsUpdater)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChangedRangeForTopLevelEditSubAction)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaddingBRElementForEmptyEditor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle)
@ -359,8 +357,6 @@ void HTMLEditor::PreDestroy(bool aDestroyingFrames) {
HideAnonymousEditingUIs();
}
mPaddingBRElementForEmptyEditor = nullptr;
EditorBase::PreDestroy(aDestroyingFrames);
}

View File

@ -962,27 +962,6 @@ class HTMLEditor final : public EditorBase,
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureCaretNotAfterPaddingBRElement();
/**
* MaybeCreatePaddingBRElementForEmptyEditor() creates padding <br> element
* for empty editor if there is no children.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeCreatePaddingBRElementForEmptyEditor();
/**
* EnsureNoPaddingBRElementForEmptyEditor() removes padding <br> element
* for empty editor if there is.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureNoPaddingBRElementForEmptyEditor();
/**
* ReflectPaddingBRElementForEmptyEditor() scans the tree from the root
* element and sets mPaddingBRElementForEmptyEditor if exists, or otherwise
* nullptr. Can be used to manage undo/redo.
*/
[[nodiscard]] nsresult ReflectPaddingBRElementForEmptyEditor();
/**
* PrepareInlineStylesForCaret() consider inline styles from top level edit
* sub-action and setting it to `mTypeInState` and clear inline style cache
@ -4311,10 +4290,6 @@ class HTMLEditor final : public EditorBase,
RefPtr<Runnable> mPendingRootElementUpdatedRunner;
RefPtr<Runnable> mPendingDocumentModifiedRunner;
// mPaddingBRElementForEmptyEditor should be used for placing caret
// at proper position when editor is empty.
RefPtr<dom::HTMLBRElement> mPaddingBRElementForEmptyEditor;
bool mCRInParagraphCreatesParagraph;
bool mCSSAware;

View File

@ -1100,7 +1100,9 @@ EditActionResult HTMLEditor::HandleDeleteSelection(
// CreateStyleForInsertText()
TopLevelEditSubActionDataRef().mDidDeleteSelection = true;
if (IsEmpty()) {
// If there is only padding `<br>` element for empty editor, cancel the
// operation.
if (mPaddingBRElementForEmptyEditor) {
return EditActionCanceled();
}
@ -1398,7 +1400,9 @@ EditActionResult HTMLEditor::AutoDeleteRangesHandler::Run(
mOriginalDirectionAndAmount = aDirectionAndAmount;
mOriginalStripWrappers = aStripWrappers;
if (aHTMLEditor.IsEmpty()) {
// If there is only padding `<br>` element for empty editor, cancel the
// operation.
if (aHTMLEditor.mPaddingBRElementForEmptyEditor) {
return EditActionCanceled();
}

View File

@ -114,6 +114,11 @@ nsresult TextEditor::OnEndHandlingTopLevelEditSubAction() {
break;
}
if (NS_FAILED(rv = EnsurePaddingBRElementForEmptyEditor())) {
NS_WARNING("TextEditor::EnsurePaddingBRElementForEmptyEditor() failed");
break;
}
if (!IsSingleLineEditor() &&
NS_FAILED(rv = EnsurePaddingBRElementInMultilineEditor())) {
NS_WARNING(
@ -175,6 +180,12 @@ EditActionResult TextEditor::InsertLineFeedCharacterAtSelection() {
}
}
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::EnsureNoPaddingBRElementForEmptyEditor() failed");
return EditActionIgnored(rv);
}
// get the (collapsed) selection location
const nsRange* firstRange = SelectionRef().GetRangeAt(0);
if (NS_WARN_IF(!firstRange)) {
@ -198,8 +209,8 @@ EditActionResult TextEditor::InsertLineFeedCharacterAtSelection() {
// Insert a linefeed character.
EditorRawDOMPoint pointAfterInsertedLineFeed;
nsresult rv = InsertTextWithTransaction(*document, u"\n"_ns, pointToInsert,
&pointAfterInsertedLineFeed);
rv = InsertTextWithTransaction(*document, u"\n"_ns, pointToInsert,
&pointAfterInsertedLineFeed);
if (!pointAfterInsertedLineFeed.IsSet()) {
NS_WARNING(
"EditorBase::InsertTextWithTransaction(\\n) didn't return position of "
@ -399,6 +410,12 @@ EditActionResult TextEditor::HandleInsertText(
MaybeDoAutoPasswordMasking();
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::EnsureNoPaddingBRElementForEmptyEditor() failed");
return EditActionHandled(rv);
}
// People have lots of different ideas about what text fields
// should do with multiline pastes. See bugs 21032, 23485, 23485, 50935.
// The six possible options are:
@ -528,6 +545,12 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
MaybeDoAutoPasswordMasking();
nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::EnsureNoPaddingBRElementForEmptyEditor() failed");
return EditActionResult(rv);
}
RefPtr<Element> anonymousDivElement = GetRoot();
RefPtr<Text> textNode =
Text::FromNodeOrNull(anonymousDivElement->GetFirstChild());
@ -556,7 +579,7 @@ EditActionResult TextEditor::SetTextWithoutTransaction(
HandleNewLinesInStringForSingleLineEditor(sanitizedValue);
}
nsresult rv = SetTextNodeWithoutTransaction(sanitizedValue, *textNode);
rv = SetTextNodeWithoutTransaction(sanitizedValue, *textNode);
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::SetTextNodeWithoutTransaction() failed");
return EditActionResult(rv);

View File

@ -329,6 +329,10 @@ already_AddRefed<Element> TextEditor::GetInputEventTargetElement() const {
}
bool TextEditor::IsEmpty() const {
if (mPaddingBRElementForEmptyEditor) {
return true;
}
// Even if there is no padding <br> element for empty editor, we should be
// detected as empty editor if all the children are text nodes and these
// have no content.
@ -539,6 +543,12 @@ nsresult TextEditor::InsertWithQuotationsAsSubAction(
// also in single line editor)?
MaybeDoAutoPasswordMasking();
rv = EnsureNoPaddingBRElementForEmptyEditor();
if (NS_FAILED(rv)) {
NS_WARNING("EditorBase::EnsureNoPaddingBRElementForEmptyEditor() failed");
return rv;
}
rv = InsertTextAsSubAction(quotedStuff);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"EditorBase::InsertTextAsSubAction() failed");
@ -613,6 +623,58 @@ nsresult TextEditor::RemoveAttributeOrEquivalent(Element* aElement,
return EditorBase::ToGenericNSResult(rv);
}
nsresult TextEditor::EnsurePaddingBRElementForEmptyEditor() {
MOZ_ASSERT(IsEditActionDataAvailable());
// If there is padding <br> element for empty editor, we have no work to do.
if (mPaddingBRElementForEmptyEditor) {
return NS_OK;
}
// Likewise, nothing to be done if we could never have inserted a trailing
// <br> element.
// XXX Why don't we use same path for <textarea> and <input>?
if (IsSingleLineEditor()) {
nsresult rv = MaybeCreatePaddingBRElementForEmptyEditor();
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() failed");
return rv;
}
if (NS_WARN_IF(!mRootElement)) {
return NS_ERROR_FAILURE;
}
uint32_t childCount = mRootElement->GetChildCount();
if (childCount == 0) {
nsresult rv = MaybeCreatePaddingBRElementForEmptyEditor();
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"EditorBase::MaybeCreatePaddingBRElementForEmptyEditor() failed");
return rv;
}
if (childCount > 1) {
return NS_OK;
}
RefPtr<HTMLBRElement> brElement =
HTMLBRElement::FromNodeOrNull(mRootElement->GetFirstChild());
if (!brElement ||
!EditorUtils::IsPaddingBRElementForEmptyLastLine(*brElement)) {
return NS_OK;
}
// Rather than deleting this node from the DOM tree we should instead
// morph this <br> element into the padding <br> element for editor.
mPaddingBRElementForEmptyEditor = std::move(brElement);
mPaddingBRElementForEmptyEditor->UnsetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
mPaddingBRElementForEmptyEditor->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
return NS_OK;
}
nsresult TextEditor::SetUnmaskRangeInternal(uint32_t aStart, uint32_t aLength,
uint32_t aTimeout, bool aNotify,
bool aForceStartMasking) {

View File

@ -421,6 +421,13 @@ class TextEditor : public EditorBase, public nsITimerCallback, public nsINamed {
MOZ_CAN_RUN_SCRIPT virtual nsresult OnEndHandlingTopLevelEditSubAction()
override;
/**
* EnsurePaddingBRElementForEmptyEditor() creates padding <br> element for
* empty editor or changes padding <br> element for empty last line to for
* empty editor when we're empty.
*/
MOZ_CAN_RUN_SCRIPT nsresult EnsurePaddingBRElementForEmptyEditor();
/**
* HandleInlineSpellCheckAfterEdit() does spell-check after handling top level
* edit subaction.