зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1898408 - Make our editor disconnect `<br>` element temporarily when its type is changed from or to padding one r=m_kato
Currently, the `<br>` element type -- whether normal `<br>` element or padding `<br>` element for empty editor or last line -- is managed by the flags of `nsINode`. Therefore, changing the flag does not cause mutation, so `IMEContentObserver` cannot observe the type changes. However, `ContentEventHandler` treats the padding `<br>` elements as invisible. Therefore, when a `<br>` element becomes a padding one, `IMEContentObserver` needs to notify IME of atext removed notification, and also when a `<br>` element becomes a normal one (i.e., visible), `IMEContentObserver` needs to notify IME of a text added notification. Therefore, this patch makes `EditorBase` disconnect the `<br>` element temporarily to make `IMEContentObserver` observable the type change. Depends on D211698 Differential Revision: https://phabricator.services.mozilla.com/D211699
This commit is contained in:
Родитель
cb97c62342
Коммит
ed99e91ef8
|
@ -16,8 +16,9 @@
|
|||
#include "DeleteNodeTransaction.h"
|
||||
#include "DeleteRangeTransaction.h"
|
||||
#include "DeleteTextTransaction.h"
|
||||
#include "EditAction.h" // for EditSubAction
|
||||
#include "EditorDOMPoint.h" // for EditorDOMPoint
|
||||
#include "EditAction.h" // for EditSubAction
|
||||
#include "EditorDOMPoint.h" // for EditorDOMPoint
|
||||
#include "EditorForwards.h"
|
||||
#include "EditorUtils.h" // for various helper classes.
|
||||
#include "EditTransactionBase.h" // for EditTransactionBase
|
||||
#include "EditorEventListener.h" // for EditorEventListener
|
||||
|
@ -35,6 +36,7 @@
|
|||
#include "gfxFontUtils.h" // for gfxFontUtils
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/EditorDOMPoint.h"
|
||||
#include "mozilla/intl/BidiEmbeddingLevel.h"
|
||||
#include "mozilla/BasePrincipal.h" // for BasePrincipal
|
||||
#include "mozilla/CheckedInt.h" // for CheckedInt
|
||||
|
@ -2414,11 +2416,17 @@ EditorBase::InsertPaddingBRElementForEmptyLastLineWithTransaction(
|
|||
pointToInsert = maybePointToInsert.unwrap();
|
||||
}
|
||||
|
||||
RefPtr<Element> newBRElement = CreateHTMLContent(nsGkAtoms::br);
|
||||
RefPtr<HTMLBRElement> newBRElement =
|
||||
HTMLBRElement::FromNodeOrNull(RefPtr{CreateHTMLContent(nsGkAtoms::br)});
|
||||
if (NS_WARN_IF(!newBRElement)) {
|
||||
return Err(NS_ERROR_FAILURE);
|
||||
}
|
||||
newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
|
||||
nsresult rv = UpdateBRElementType(*newBRElement,
|
||||
BRElementType::PaddingForEmptyLastLine);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::UpdateBRElementType() failed");
|
||||
return Err(rv);
|
||||
}
|
||||
|
||||
Result<CreateElementResult, nsresult> insertBRElementResult =
|
||||
InsertNodeWithTransaction<Element>(*newBRElement, pointToInsert);
|
||||
|
@ -2427,6 +2435,68 @@ EditorBase::InsertPaddingBRElementForEmptyLastLineWithTransaction(
|
|||
return insertBRElementResult;
|
||||
}
|
||||
|
||||
nsresult EditorBase::UpdateBRElementType(HTMLBRElement& aBRElement,
|
||||
BRElementType aNewType) {
|
||||
const bool brElementIsHidden = aBRElement.IsPaddingForEmptyEditor() ||
|
||||
aBRElement.IsPaddingForEmptyLastLine();
|
||||
const bool brElementWillBeHidden = aNewType != BRElementType::Normal;
|
||||
const auto SetBRElementFlags = [&]() {
|
||||
switch (aNewType) {
|
||||
case BRElementType::Normal:
|
||||
if (brElementIsHidden) {
|
||||
aBRElement.UnsetFlags(NS_PADDING_FOR_EMPTY_EDITOR |
|
||||
NS_PADDING_FOR_EMPTY_LAST_LINE);
|
||||
}
|
||||
break;
|
||||
case BRElementType::PaddingForEmptyEditor:
|
||||
if (brElementIsHidden) {
|
||||
aBRElement.UnsetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
|
||||
}
|
||||
aBRElement.SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
|
||||
break;
|
||||
case BRElementType::PaddingForEmptyLastLine:
|
||||
if (brElementIsHidden) {
|
||||
aBRElement.UnsetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
|
||||
}
|
||||
aBRElement.SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
|
||||
break;
|
||||
}
|
||||
};
|
||||
// If the <br> element is in the composed doc, it must be observed by
|
||||
// IMEContentObserver. However, IMEContentObserver cannot observe the state
|
||||
// change, but changing the <br> type may make the <br> element visible or
|
||||
// invisible for ContentEventHandler. Therefore, IMEContentObserver needs to
|
||||
// notify IME of the state change as a text change notification of adding or
|
||||
// removing a line break. Therefore, we need to reconnect the <br> element
|
||||
// temporarily for making IMEContentObserver observable this change.
|
||||
if (!aBRElement.IsInComposedDoc() ||
|
||||
brElementIsHidden == brElementWillBeHidden) {
|
||||
SetBRElementFlags();
|
||||
return NS_OK;
|
||||
}
|
||||
EditorDOMPoint pointToInsert(&aBRElement);
|
||||
{
|
||||
AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
|
||||
nsresult rv = DeleteNodeWithTransaction(aBRElement);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::DeleteNodeWithTransaction() failed");
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (NS_WARN_IF(!pointToInsert.IsSetAndValid())) {
|
||||
return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
|
||||
}
|
||||
SetBRElementFlags();
|
||||
Result<CreateElementResult, nsresult> result =
|
||||
InsertNodeWithTransaction<Element>(aBRElement, pointToInsert);
|
||||
if (MOZ_UNLIKELY(result.isErr())) {
|
||||
NS_WARNING("EditorBase::InsertNodeWithTransaction() failed");
|
||||
return result.unwrapErr();
|
||||
}
|
||||
result.inspect().IgnoreCaretPointSuggestion();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EditorBase::DeleteNode(nsINode* aNode, bool aPreserveSelection,
|
||||
uint8_t aOptionalArgCount) {
|
||||
MOZ_ASSERT_UNREACHABLE("Do not use this API with TextEditor");
|
||||
|
@ -3654,8 +3724,12 @@ nsresult EditorBase::EnsurePaddingBRElementInMultilineEditor() {
|
|||
}
|
||||
|
||||
// Morph it back to a padding <br> element for empty last line.
|
||||
brElement->UnsetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
|
||||
brElement->SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
|
||||
nsresult rv =
|
||||
UpdateBRElementType(*brElement, BRElementType::PaddingForEmptyLastLine);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::UpdateBRElementType() failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -1754,6 +1754,19 @@ class EditorBase : public nsIEditor,
|
|||
InsertPaddingBRElementForEmptyLastLineWithTransaction(
|
||||
const EditorDOMPoint& aPointToInsert);
|
||||
|
||||
enum class BRElementType {
|
||||
Normal,
|
||||
PaddingForEmptyEditor,
|
||||
PaddingForEmptyLastLine
|
||||
};
|
||||
/**
|
||||
* Updates the type of aBRElement. If it will be hidden or shown from
|
||||
* IMEContentObserver and ContentEventHandler points of view, this temporarily
|
||||
* removes the node and reconnect to the same position.
|
||||
*/
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
|
||||
UpdateBRElementType(dom::HTMLBRElement& aBRElement, BRElementType aNewType);
|
||||
|
||||
/**
|
||||
* CloneAttributesWithTransaction() clones all attributes from
|
||||
* aSourceElement to aDestElement after removing all attributes in
|
||||
|
|
|
@ -974,7 +974,8 @@ nsresult HTMLEditor::MaybeCreatePaddingBRElementForEmptyEditor() {
|
|||
"HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
|
||||
|
||||
// Create a br.
|
||||
RefPtr<Element> newBRElement = CreateHTMLContent(nsGkAtoms::br);
|
||||
RefPtr<HTMLBRElement> newBRElement =
|
||||
HTMLBRElement::FromNodeOrNull(RefPtr{CreateHTMLContent(nsGkAtoms::br)});
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -982,11 +983,15 @@ nsresult HTMLEditor::MaybeCreatePaddingBRElementForEmptyEditor() {
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mPaddingBRElementForEmptyEditor =
|
||||
static_cast<HTMLBRElement*>(newBRElement.get());
|
||||
mPaddingBRElementForEmptyEditor = newBRElement;
|
||||
|
||||
// Give it a special attribute.
|
||||
newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
|
||||
nsresult rv =
|
||||
UpdateBRElementType(*newBRElement, BRElementType::PaddingForEmptyEditor);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::UpdateBRElementType() failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Put the node in the document.
|
||||
Result<CreateElementResult, nsresult> insertBRElementResult =
|
||||
|
@ -999,7 +1004,7 @@ nsresult HTMLEditor::MaybeCreatePaddingBRElementForEmptyEditor() {
|
|||
|
||||
// Set selection.
|
||||
insertBRElementResult.inspect().IgnoreCaretPointSuggestion();
|
||||
nsresult rv = CollapseSelectionToStartOf(*bodyOrDocumentElement);
|
||||
rv = CollapseSelectionToStartOf(*bodyOrDocumentElement);
|
||||
if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
||||
NS_WARNING(
|
||||
"EditorBase::CollapseSelectionToStartOf() caused destroying the "
|
||||
|
@ -8698,7 +8703,12 @@ Result<SplitNodeResult, nsresult> HTMLEditor::SplitParagraphWithTransaction(
|
|||
// <br>.
|
||||
if (brElement &&
|
||||
brElement->GetParentNode() == deepestInlineContainerElement) {
|
||||
brElement->SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
|
||||
nsresult rv = UpdateBRElementType(
|
||||
*brElement, BRElementType::PaddingForEmptyLastLine);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("EditorBase::UpdateBRElementType() failed");
|
||||
return Err(rv);
|
||||
}
|
||||
return SplitNodeResult(std::move(unwrappedSplitDivOrPResult),
|
||||
EditorDOMPoint(brElement));
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче